commit
4f4d024418
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -4,7 +4,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- plugins
|
||||
- bug-fixes
|
||||
pull_request: {}
|
||||
|
||||
jobs:
|
||||
|
28
CHANGELOG.md
28
CHANGELOG.md
@ -1,6 +1,32 @@
|
||||
Rhai Release Notes
|
||||
==================
|
||||
|
||||
Version 1.1.0
|
||||
=============
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
|
||||
* Custom syntax starting with a disabled standard keyword now works properly.
|
||||
* When calling `Engine::call_fn`, new variables defined during evaluation of the body script are removed and no longer spill into the function call.
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
|
||||
* `Engine::consume_XXX` methods are renamed to `Engine::run_XXX` to make meanings clearer. The `consume_XXX` API is deprecated.
|
||||
* `$symbol$` is supported in custom syntax to match any symbol.
|
||||
* Custom syntax with `$block$`, `}` or `;` as the last symbol are now self-terminating (i.e. no need to attach a terminating `;`).
|
||||
* `Dynamic::as_string` and `Dynamic::as_immutable_string` are deprecated and replaced by `into_string` and `into_immutable_string` respectively.
|
||||
* Added a number of constants to `Dynamic`.
|
||||
* Added a number of constants and `fromXXX` constant methods to `Dynamic`.
|
||||
* `parse_float()`, `PI()` and `E()` now defer to `Decimal` under `no_float` if `decimal` is turned on.
|
||||
* Added `log10()` for `Decimal`.
|
||||
* `ln` for `Decimal` is now checked and won't panic.
|
||||
* `Scope::set_value` now takes anything that implements `Into<Cow<str>>`.
|
||||
* Added `Scope::is_constant` to check if a variable is constant.
|
||||
* Added `Scope::set_or_push` to add a new variable only if one doesn't already exist.
|
||||
|
||||
|
||||
Version 1.0.2
|
||||
=============
|
||||
|
||||
@ -105,7 +131,7 @@ New features
|
||||
* Each `Dynamic` value can now contain arbitrary data (type `i32`) in the form of a _tag_. This is to use up otherwise wasted space in the `Dynamic` type.
|
||||
* A new internal feature `no_smartstring` to turn off `SmartString` for those rare cases that it is needed.
|
||||
* `DynamicReadLock` and `DynamicWriteLoc` are exposed under `internals`.
|
||||
* `From<Shared<Locked<Dynamic>>>` is added for `Dynamic` mapping directly to a shared value, together with support for `Dynamic::from`.
|
||||
* `From< Shared< Locked<Dynamic> > >` is added for `Dynamic` mapping directly to a shared value, together with support for `Dynamic::from`.
|
||||
* An indexer with string index acts as a _fallback_ to a property getter/setter.
|
||||
|
||||
Enhancements
|
||||
|
@ -3,7 +3,7 @@ members = [".", "codegen"]
|
||||
|
||||
[package]
|
||||
name = "rhai"
|
||||
version = "1.0.3"
|
||||
version = "1.1.0"
|
||||
edition = "2018"
|
||||
authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung", "jhwgh1968"]
|
||||
description = "Embedded scripting for Rust"
|
||||
@ -92,7 +92,7 @@ default-features = false
|
||||
optional = true
|
||||
|
||||
[dependencies.rust_decimal]
|
||||
version = "1.14.2"
|
||||
version = "1.15"
|
||||
default-features = false
|
||||
features = ["maths"]
|
||||
optional = true
|
||||
|
@ -15,7 +15,7 @@ fn bench_eval_array_small_get(bench: &mut Bencher) {
|
||||
|
||||
let ast = engine.compile(script).unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast(&ast).unwrap());
|
||||
bench.iter(|| engine.run_ast(&ast).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
@ -27,7 +27,7 @@ fn bench_eval_array_small_set(bench: &mut Bencher) {
|
||||
|
||||
let ast = engine.compile(script).unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast(&ast).unwrap());
|
||||
bench.iter(|| engine.run_ast(&ast).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
@ -43,7 +43,7 @@ fn bench_eval_array_large_get(bench: &mut Bencher) {
|
||||
|
||||
let ast = engine.compile(script).unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast(&ast).unwrap());
|
||||
bench.iter(|| engine.run_ast(&ast).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
@ -59,7 +59,7 @@ fn bench_eval_array_large_set(bench: &mut Bencher) {
|
||||
|
||||
let ast = engine.compile(script).unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast(&ast).unwrap());
|
||||
bench.iter(|| engine.run_ast(&ast).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
@ -83,5 +83,5 @@ fn bench_eval_array_loop(bench: &mut Bencher) {
|
||||
|
||||
let ast = engine.compile(script).unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast(&ast).unwrap());
|
||||
bench.iter(|| engine.run_ast(&ast).unwrap());
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ fn bench_eval_expression_single(bench: &mut Bencher) {
|
||||
|
||||
let ast = engine.compile_expression(script).unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast(&ast).unwrap());
|
||||
bench.iter(|| engine.run_ast(&ast).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
@ -27,7 +27,7 @@ fn bench_eval_expression_number_literal(bench: &mut Bencher) {
|
||||
|
||||
let ast = engine.compile_expression(script).unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast(&ast).unwrap());
|
||||
bench.iter(|| engine.run_ast(&ast).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
@ -39,7 +39,7 @@ fn bench_eval_expression_number_operators(bench: &mut Bencher) {
|
||||
|
||||
let ast = engine.compile_expression(script).unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast(&ast).unwrap());
|
||||
bench.iter(|| engine.run_ast(&ast).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
@ -56,7 +56,7 @@ fn bench_eval_expression_optimized_simple(bench: &mut Bencher) {
|
||||
engine.set_optimization_level(OptimizationLevel::Simple);
|
||||
let ast = engine.compile_expression(script).unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast(&ast).unwrap());
|
||||
bench.iter(|| engine.run_ast(&ast).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
@ -73,7 +73,7 @@ fn bench_eval_expression_optimized_full(bench: &mut Bencher) {
|
||||
engine.set_optimization_level(OptimizationLevel::Full);
|
||||
let ast = engine.compile_expression(script).unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast(&ast).unwrap());
|
||||
bench.iter(|| engine.run_ast(&ast).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
@ -120,7 +120,7 @@ fn bench_eval_loop_number(bench: &mut Bencher) {
|
||||
|
||||
let ast = engine.compile(script).unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast(&ast).unwrap());
|
||||
bench.iter(|| engine.run_ast(&ast).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
@ -137,7 +137,7 @@ fn bench_eval_loop_strings_build(bench: &mut Bencher) {
|
||||
|
||||
let ast = engine.compile(script).unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast(&ast).unwrap());
|
||||
bench.iter(|| engine.run_ast(&ast).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
@ -154,7 +154,7 @@ fn bench_eval_loop_strings_no_build(bench: &mut Bencher) {
|
||||
|
||||
let ast = engine.compile(script).unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast(&ast).unwrap());
|
||||
bench.iter(|| engine.run_ast(&ast).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
@ -186,7 +186,7 @@ fn bench_eval_switch(bench: &mut Bencher) {
|
||||
|
||||
let ast = engine.compile(script).unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast(&ast).unwrap());
|
||||
bench.iter(|| engine.run_ast(&ast).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
@ -216,5 +216,5 @@ fn bench_eval_nested_if(bench: &mut Bencher) {
|
||||
|
||||
let ast = engine.compile(script).unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast(&ast).unwrap());
|
||||
bench.iter(|| engine.run_ast(&ast).unwrap());
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ fn bench_eval_map_small_get(bench: &mut Bencher) {
|
||||
|
||||
let ast = engine.compile(script).unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast(&ast).unwrap());
|
||||
bench.iter(|| engine.run_ast(&ast).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
@ -27,7 +27,7 @@ fn bench_eval_map_small_set(bench: &mut Bencher) {
|
||||
|
||||
let ast = engine.compile(script).unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast(&ast).unwrap());
|
||||
bench.iter(|| engine.run_ast(&ast).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
@ -47,7 +47,7 @@ fn bench_eval_map_large_get(bench: &mut Bencher) {
|
||||
|
||||
let ast = engine.compile(script).unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast(&ast).unwrap());
|
||||
bench.iter(|| engine.run_ast(&ast).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
@ -67,5 +67,5 @@ fn bench_eval_map_large_set(bench: &mut Bencher) {
|
||||
|
||||
let ast = engine.compile(script).unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast(&ast).unwrap());
|
||||
bench.iter(|| engine.run_ast(&ast).unwrap());
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ fn bench_eval_module(bench: &mut Bencher) {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast(&ast).unwrap());
|
||||
bench.iter(|| engine.run_ast(&ast).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
@ -49,5 +49,5 @@ fn bench_eval_function_call(bench: &mut Bencher) {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast(&ast).unwrap());
|
||||
bench.iter(|| engine.run_ast(&ast).unwrap());
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ fn bench_eval_scope_single(bench: &mut Bencher) {
|
||||
|
||||
let ast = engine.compile_expression(script).unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast_with_scope(&mut scope, &ast).unwrap());
|
||||
bench.iter(|| engine.run_ast_with_scope(&mut scope, &ast).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
@ -34,7 +34,7 @@ fn bench_eval_scope_multiple(bench: &mut Bencher) {
|
||||
|
||||
let ast = engine.compile_expression(script).unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast_with_scope(&mut scope, &ast).unwrap());
|
||||
bench.iter(|| engine.run_ast_with_scope(&mut scope, &ast).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
@ -50,7 +50,7 @@ fn bench_eval_scope_longer(bench: &mut Bencher) {
|
||||
|
||||
let ast = engine.compile_expression(script).unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast_with_scope(&mut scope, &ast).unwrap());
|
||||
bench.iter(|| engine.run_ast_with_scope(&mut scope, &ast).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
@ -73,5 +73,5 @@ fn bench_eval_scope_complex(bench: &mut Bencher) {
|
||||
|
||||
let ast = engine.compile_expression(script).unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast_with_scope(&mut scope, &ast).unwrap());
|
||||
bench.iter(|| engine.run_ast_with_scope(&mut scope, &ast).unwrap());
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ fn bench_type_field(bench: &mut Bencher) {
|
||||
let mut scope = Scope::new();
|
||||
scope.push("foo", Test { x: 42 });
|
||||
|
||||
bench.iter(|| engine.consume_ast_with_scope(&mut scope, &ast).unwrap());
|
||||
bench.iter(|| engine.run_ast_with_scope(&mut scope, &ast).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
@ -59,7 +59,7 @@ fn bench_type_method(bench: &mut Bencher) {
|
||||
let mut scope = Scope::new();
|
||||
scope.push("foo", Test { x: 42 });
|
||||
|
||||
bench.iter(|| engine.consume_ast_with_scope(&mut scope, &ast).unwrap());
|
||||
bench.iter(|| engine.run_ast_with_scope(&mut scope, &ast).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
@ -77,7 +77,7 @@ fn bench_type_method_with_params(bench: &mut Bencher) {
|
||||
let mut scope = Scope::new();
|
||||
scope.push("foo", Test { x: 42 });
|
||||
|
||||
bench.iter(|| engine.consume_ast_with_scope(&mut scope, &ast).unwrap());
|
||||
bench.iter(|| engine.run_ast_with_scope(&mut scope, &ast).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
@ -96,5 +96,5 @@ fn bench_type_method_nested(bench: &mut Bencher) {
|
||||
let mut scope = Scope::new();
|
||||
scope.push("foo", Test { x: 42 });
|
||||
|
||||
bench.iter(|| engine.consume_ast_with_scope(&mut scope, &ast).unwrap());
|
||||
bench.iter(|| engine.run_ast_with_scope(&mut scope, &ast).unwrap());
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ fn bench_iterations_1000(bench: &mut Bencher) {
|
||||
|
||||
let ast = engine.compile(script).unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast(&ast).unwrap());
|
||||
bench.iter(|| engine.run_ast(&ast).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
@ -39,5 +39,5 @@ fn bench_eval_primes(bench: &mut Bencher) {
|
||||
|
||||
let ast = engine.compile(SCRIPT).unwrap();
|
||||
|
||||
bench.iter(|| engine.consume_ast(&ast).unwrap());
|
||||
bench.iter(|| engine.run_ast(&ast).unwrap());
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rhai_codegen"
|
||||
version = "1.0.0"
|
||||
version = "1.1.0"
|
||||
edition = "2018"
|
||||
authors = ["jhwgh1968", "Stephen Chung"]
|
||||
description = "Procedural macros support package for Rhai, a scripting language and engine for Rust"
|
||||
|
@ -743,7 +743,7 @@ impl ExportedFn {
|
||||
is_string = true;
|
||||
is_ref = true;
|
||||
quote_spanned!(arg_type.span() =>
|
||||
mem::take(args[#i]).as_immutable_string().unwrap()
|
||||
mem::take(args[#i]).into_immutable_string().unwrap()
|
||||
)
|
||||
}
|
||||
_ => panic!("internal error: why wasn't this found earlier!?"),
|
||||
@ -752,7 +752,7 @@ impl ExportedFn {
|
||||
is_string = true;
|
||||
is_ref = false;
|
||||
quote_spanned!(arg_type.span() =>
|
||||
mem::take(args[#i]).as_string().unwrap()
|
||||
mem::take(args[#i]).into_string().unwrap()
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
|
@ -525,7 +525,7 @@ mod generate_tests {
|
||||
impl PluginFunction for Token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
let arg0 = mem::take(args[0usize]).as_immutable_string().unwrap();
|
||||
let arg0 = mem::take(args[0usize]).into_immutable_string().unwrap();
|
||||
Ok(Dynamic::from(special_print(&arg0)))
|
||||
}
|
||||
|
||||
|
@ -934,7 +934,7 @@ mod generate_tests {
|
||||
impl PluginFunction for print_out_to_token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
let arg0 = mem::take(args[0usize]).as_immutable_string().unwrap();
|
||||
let arg0 = mem::take(args[0usize]).into_immutable_string().unwrap();
|
||||
Ok(Dynamic::from(print_out_to(&arg0)))
|
||||
}
|
||||
|
||||
@ -987,7 +987,7 @@ mod generate_tests {
|
||||
impl PluginFunction for print_out_to_token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
let arg0 = mem::take(args[0usize]).as_string().unwrap();
|
||||
let arg0 = mem::take(args[0usize]).into_string().unwrap();
|
||||
Ok(Dynamic::from(print_out_to(arg0)))
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,8 @@ error[E0277]: the trait bound `NonClonable: Clone` is not satisfied
|
||||
11 | pub fn test_fn(input: f32) -> NonClonable {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `NonClonable`
|
||||
|
|
||||
::: $WORKSPACE/src/dynamic.rs
|
||||
note: required by a bound in `rhai::Dynamic::from`
|
||||
--> $DIR/dynamic.rs:1127:30
|
||||
|
|
||||
| pub fn from<T: Variant + Clone>(mut value: T) -> Self {
|
||||
| ----- required by this bound in `rhai::Dynamic::from`
|
||||
1127 | pub fn from<T: Variant + Clone>(mut value: T) -> Self {
|
||||
| ^^^^^ required by this bound in `rhai::Dynamic::from`
|
||||
|
@ -4,7 +4,8 @@ error[E0277]: the trait bound `NonClonable: Clone` is not satisfied
|
||||
12 | pub fn test_fn(input: f32) -> NonClonable {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `NonClonable`
|
||||
|
|
||||
::: $WORKSPACE/src/dynamic.rs
|
||||
note: required by a bound in `rhai::Dynamic::from`
|
||||
--> $DIR/dynamic.rs:1127:30
|
||||
|
|
||||
| pub fn from<T: Variant + Clone>(mut value: T) -> Self {
|
||||
| ----- required by this bound in `rhai::Dynamic::from`
|
||||
1127 | pub fn from<T: Variant + Clone>(mut value: T) -> Self {
|
||||
| ^^^^^ required by this bound in `rhai::Dynamic::from`
|
||||
|
@ -3,7 +3,7 @@ use rhai::{Engine, EvalAltResult, INT};
|
||||
fn main() -> Result<(), Box<EvalAltResult>> {
|
||||
let engine = Engine::new();
|
||||
|
||||
engine.consume(r#"print("hello, world!")"#)?;
|
||||
engine.run(r#"print("hello, world!")"#)?;
|
||||
|
||||
let result = engine.eval::<INT>("40 + 2")?;
|
||||
|
||||
|
@ -61,7 +61,7 @@ fn main() -> Result<(), Box<EvalAltResult>> {
|
||||
|
||||
println!("Line: {}", input.replace('\r', "\\r").replace('\n', "\\n"));
|
||||
|
||||
engine.consume_with_scope(
|
||||
engine.run_with_scope(
|
||||
&mut scope,
|
||||
r#"
|
||||
display("Length", x.len());
|
||||
|
@ -34,7 +34,7 @@ fn main() {
|
||||
|
||||
// Run script
|
||||
engine
|
||||
.consume(
|
||||
.run(
|
||||
r#"
|
||||
print("Starting script loop...");
|
||||
|
||||
|
21
src/ast.rs
21
src/ast.rs
@ -1263,10 +1263,12 @@ impl Stmt {
|
||||
// A No-op requires a semicolon in order to know it is an empty statement!
|
||||
Self::Noop(_) => false,
|
||||
|
||||
Self::Expr(Expr::Custom(x, _)) if x.is_self_terminated() => true,
|
||||
|
||||
Self::Var(_, _, _, _)
|
||||
| Self::Assignment(_, _)
|
||||
| Self::FnCall(_, _)
|
||||
| Self::Expr(_)
|
||||
| Self::FnCall(_, _)
|
||||
| Self::Do(_, _, _, _)
|
||||
| Self::Continue(_)
|
||||
| Self::Break(_)
|
||||
@ -1509,6 +1511,19 @@ pub struct CustomExpr {
|
||||
pub scope_may_be_changed: bool,
|
||||
/// List of tokens actually parsed.
|
||||
pub tokens: StaticVec<Identifier>,
|
||||
/// Is this custom syntax self-terminated?
|
||||
pub self_terminated: bool,
|
||||
}
|
||||
|
||||
impl CustomExpr {
|
||||
/// Is this custom syntax self-terminated (i.e. no need for a semicolon terminator)?
|
||||
///
|
||||
/// A self-terminated custom syntax always ends in `$block$`, `}` or `;`
|
||||
#[must_use]
|
||||
#[inline(always)]
|
||||
pub const fn is_self_terminated(&self) -> bool {
|
||||
self.self_terminated
|
||||
}
|
||||
}
|
||||
|
||||
/// _(internals)_ A binary expression.
|
||||
@ -1552,8 +1567,8 @@ impl OpAssignment<'_> {
|
||||
let op_raw = op
|
||||
.map_op_assignment()
|
||||
.expect("never fails because token must be an op-assignment operator")
|
||||
.keyword_syntax();
|
||||
let op_assignment = op.keyword_syntax();
|
||||
.literal_syntax();
|
||||
let op_assignment = op.literal_syntax();
|
||||
|
||||
Self {
|
||||
hash_op_assign: calc_fn_hash(op_assignment, 2),
|
||||
|
@ -90,7 +90,7 @@ fn main() {
|
||||
.map_err(|err| err.into())
|
||||
.and_then(|mut ast| {
|
||||
ast.set_source(filename.to_string_lossy().to_string());
|
||||
engine.consume_ast(&ast)
|
||||
engine.run_ast(&ast)
|
||||
})
|
||||
{
|
||||
let filename = filename.to_string_lossy();
|
||||
|
@ -14,21 +14,26 @@ use std::any::TypeId;
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
/// Special marker for matching an expression.
|
||||
pub const CUSTOM_SYNTAX_MARKER_EXPR: &str = "$expr$";
|
||||
/// Special marker for matching a statements block.
|
||||
pub const CUSTOM_SYNTAX_MARKER_BLOCK: &str = "$block$";
|
||||
/// Special marker for matching an identifier.
|
||||
pub const CUSTOM_SYNTAX_MARKER_IDENT: &str = "$ident$";
|
||||
/// Special marker for matching a string literal.
|
||||
pub const CUSTOM_SYNTAX_MARKER_STRING: &str = "$string$";
|
||||
/// Special marker for matching an integer number.
|
||||
pub const CUSTOM_SYNTAX_MARKER_INT: &str = "$int$";
|
||||
/// Special marker for matching a floating-point number.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
pub const CUSTOM_SYNTAX_MARKER_FLOAT: &str = "$float$";
|
||||
/// Special marker for matching a boolean value.
|
||||
pub const CUSTOM_SYNTAX_MARKER_BOOL: &str = "$bool$";
|
||||
/// Collection of special markers for custom syntax definition.
|
||||
pub mod markers {
|
||||
/// Special marker for matching an expression.
|
||||
pub const CUSTOM_SYNTAX_MARKER_EXPR: &str = "$expr$";
|
||||
/// Special marker for matching a statements block.
|
||||
pub const CUSTOM_SYNTAX_MARKER_BLOCK: &str = "$block$";
|
||||
/// Special marker for matching an identifier.
|
||||
pub const CUSTOM_SYNTAX_MARKER_IDENT: &str = "$ident$";
|
||||
/// Special marker for matching a single symbol.
|
||||
pub const CUSTOM_SYNTAX_MARKER_SYMBOL: &str = "$symbol$";
|
||||
/// Special marker for matching a string literal.
|
||||
pub const CUSTOM_SYNTAX_MARKER_STRING: &str = "$string$";
|
||||
/// Special marker for matching an integer number.
|
||||
pub const CUSTOM_SYNTAX_MARKER_INT: &str = "$int$";
|
||||
/// Special marker for matching a floating-point number.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
pub const CUSTOM_SYNTAX_MARKER_FLOAT: &str = "$float$";
|
||||
/// Special marker for matching a boolean value.
|
||||
pub const CUSTOM_SYNTAX_MARKER_BOOL: &str = "$bool$";
|
||||
}
|
||||
|
||||
/// A general expression evaluation trait object.
|
||||
#[cfg(not(feature = "sync"))]
|
||||
@ -184,6 +189,8 @@ impl Engine {
|
||||
scope_may_be_changed: bool,
|
||||
func: impl Fn(&mut EvalContext, &[Expression]) -> RhaiResult + SendSync + 'static,
|
||||
) -> Result<&mut Self, ParseError> {
|
||||
use markers::*;
|
||||
|
||||
let mut segments: StaticVec<ImmutableString> = Default::default();
|
||||
|
||||
for s in keywords {
|
||||
@ -199,6 +206,7 @@ impl Engine {
|
||||
let seg = match s {
|
||||
// Markers not in first position
|
||||
CUSTOM_SYNTAX_MARKER_IDENT
|
||||
| CUSTOM_SYNTAX_MARKER_SYMBOL
|
||||
| CUSTOM_SYNTAX_MARKER_EXPR
|
||||
| CUSTOM_SYNTAX_MARKER_BLOCK
|
||||
| CUSTOM_SYNTAX_MARKER_BOOL
|
||||
@ -222,11 +230,10 @@ impl Engine {
|
||||
}
|
||||
s.into()
|
||||
}
|
||||
// Standard keyword in first position
|
||||
s if segments.is_empty()
|
||||
&& token
|
||||
.as_ref()
|
||||
.map_or(false, |v| v.is_keyword() || v.is_reserved()) =>
|
||||
// Standard keyword in first position but not disabled
|
||||
_ if segments.is_empty()
|
||||
&& token.as_ref().map_or(false, |v| v.is_standard_keyword())
|
||||
&& !self.disabled_symbols.contains(s) =>
|
||||
{
|
||||
return Err(LexError::ImproperSymbol(
|
||||
s.to_string(),
|
||||
@ -239,14 +246,14 @@ impl Engine {
|
||||
.into_err(Position::NONE));
|
||||
}
|
||||
// Identifier in first position
|
||||
s if segments.is_empty() && is_valid_identifier(s.chars()) => {
|
||||
_ if segments.is_empty() && is_valid_identifier(s.chars()) => {
|
||||
// Make it a custom keyword/symbol if it is disabled or reserved
|
||||
if (self.disabled_symbols.contains(s)
|
||||
|| matches!(token, Some(Token::Reserved(_))))
|
||||
&& !self.custom_keywords.contains_key(s)
|
||||
if self.disabled_symbols.contains(s) || token.map_or(false, |v| v.is_reserved())
|
||||
{
|
||||
if !self.custom_keywords.contains_key(s) {
|
||||
self.custom_keywords.insert(s.into(), None);
|
||||
}
|
||||
}
|
||||
s.into()
|
||||
}
|
||||
// Anything else is an error
|
||||
|
143
src/deprecated.rs
Normal file
143
src/deprecated.rs
Normal file
@ -0,0 +1,143 @@
|
||||
//! Module containing all deprecated API that will be removed in the next major version.
|
||||
|
||||
use crate::{Dynamic, Engine, EvalAltResult, ImmutableString, Scope, AST};
|
||||
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
impl Engine {
|
||||
/// Evaluate a file, but throw away the result and only return error (if any).
|
||||
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
||||
///
|
||||
/// Not available under `no_std` or `WASM`.
|
||||
///
|
||||
/// # Deprecated
|
||||
///
|
||||
/// This method is deprecated. Use [`run_file`][Engine::run_file] instead.
|
||||
///
|
||||
/// This method will be removed in the next major version.
|
||||
#[deprecated(since = "1.1.0", note = "use `run_file` instead")]
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
||||
#[inline(always)]
|
||||
pub fn consume_file(&self, path: std::path::PathBuf) -> Result<(), Box<EvalAltResult>> {
|
||||
self.run_file(path)
|
||||
}
|
||||
|
||||
/// Evaluate a file with own scope, but throw away the result and only return error (if any).
|
||||
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
||||
///
|
||||
/// Not available under `no_std` or `WASM`.
|
||||
///
|
||||
/// # Deprecated
|
||||
///
|
||||
/// This method is deprecated. Use [`run_file_with_scope`][Engine::run_file_with_scope] instead.
|
||||
///
|
||||
/// This method will be removed in the next major version.
|
||||
#[deprecated(since = "1.1.0", note = "use `run_file_with_scope` instead")]
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
||||
#[inline(always)]
|
||||
pub fn consume_file_with_scope(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
path: std::path::PathBuf,
|
||||
) -> Result<(), Box<EvalAltResult>> {
|
||||
self.run_file_with_scope(scope, path)
|
||||
}
|
||||
|
||||
/// Evaluate a string, but throw away the result and only return error (if any).
|
||||
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
||||
///
|
||||
/// # Deprecated
|
||||
///
|
||||
/// This method is deprecated. Use [`run`][Engine::run] instead.
|
||||
///
|
||||
/// This method will be removed in the next major version.
|
||||
#[deprecated(since = "1.1.0", note = "use `run` instead")]
|
||||
#[inline(always)]
|
||||
pub fn consume(&self, script: &str) -> Result<(), Box<EvalAltResult>> {
|
||||
self.run(script)
|
||||
}
|
||||
|
||||
/// Evaluate a string with own scope, but throw away the result and only return error (if any).
|
||||
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
||||
///
|
||||
/// # Deprecated
|
||||
///
|
||||
/// This method is deprecated. Use [`run_with_scope`][Engine::run_with_scope] instead.
|
||||
///
|
||||
/// This method will be removed in the next major version.
|
||||
#[deprecated(since = "1.1.0", note = "use `run_with_scope` instead")]
|
||||
#[inline(always)]
|
||||
pub fn consume_with_scope(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
script: &str,
|
||||
) -> Result<(), Box<EvalAltResult>> {
|
||||
self.run_with_scope(scope, script)
|
||||
}
|
||||
|
||||
/// Evaluate an AST, but throw away the result and only return error (if any).
|
||||
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
||||
///
|
||||
/// # Deprecated
|
||||
///
|
||||
/// This method is deprecated. Use [`run_ast`][Engine::run_ast] instead.
|
||||
///
|
||||
/// This method will be removed in the next major version.
|
||||
#[deprecated(since = "1.1.0", note = "use `run_ast` instead")]
|
||||
#[inline(always)]
|
||||
pub fn consume_ast(&self, ast: &AST) -> Result<(), Box<EvalAltResult>> {
|
||||
self.run_ast(ast)
|
||||
}
|
||||
|
||||
/// Evaluate an [`AST`] with own scope, but throw away the result and only return error (if any).
|
||||
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
||||
///
|
||||
/// # Deprecated
|
||||
///
|
||||
/// This method is deprecated. Use [`run_ast_with_scope`][Engine::run_ast_with_scope] instead.
|
||||
///
|
||||
/// This method will be removed in the next major version.
|
||||
#[deprecated(since = "1.1.0", note = "use `run_ast_with_scope` instead")]
|
||||
#[inline(always)]
|
||||
pub fn consume_ast_with_scope(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
ast: &AST,
|
||||
) -> Result<(), Box<EvalAltResult>> {
|
||||
self.run_ast_with_scope(scope, ast)
|
||||
}
|
||||
}
|
||||
|
||||
impl Dynamic {
|
||||
/// Convert the [`Dynamic`] into a [`String`] and return it.
|
||||
/// If there are other references to the same string, a cloned copy is returned.
|
||||
/// Returns the name of the actual type if the cast fails.
|
||||
///
|
||||
/// # Deprecated
|
||||
///
|
||||
/// This method is deprecated. Use [`into_string`][Dynamic::into_string] instead.
|
||||
///
|
||||
/// This method will be removed in the next major version.
|
||||
#[deprecated(since = "1.1.0", note = "use `into_string` instead")]
|
||||
#[inline(always)]
|
||||
pub fn as_string(self) -> Result<String, &'static str> {
|
||||
self.into_string()
|
||||
}
|
||||
|
||||
/// Convert the [`Dynamic`] into an [`ImmutableString`] and return it.
|
||||
/// Returns the name of the actual type if the cast fails.
|
||||
///
|
||||
/// # Deprecated
|
||||
///
|
||||
/// This method is deprecated. Use [`into_immutable_string`][Dynamic::into_immutable_string] instead.
|
||||
///
|
||||
/// This method will be removed in the next major version.
|
||||
#[deprecated(since = "1.1.0", note = "use `into_immutable_string` instead")]
|
||||
#[inline(always)]
|
||||
pub fn as_immutable_string(self) -> Result<ImmutableString, &'static str> {
|
||||
self.into_immutable_string()
|
||||
}
|
||||
}
|
164
src/dynamic.rs
164
src/dynamic.rs
@ -10,6 +10,7 @@ use std::{
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
ops::{Deref, DerefMut},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
@ -819,66 +820,149 @@ impl Default for Dynamic {
|
||||
|
||||
impl Dynamic {
|
||||
/// A [`Dynamic`] containing a `()`.
|
||||
pub const UNIT: Dynamic = Self(Union::Unit((), DEFAULT_TAG_VALUE, ReadWrite));
|
||||
pub const UNIT: Self = Self(Union::Unit((), DEFAULT_TAG_VALUE, ReadWrite));
|
||||
/// A [`Dynamic`] containing a `true`.
|
||||
pub const TRUE: Dynamic = Self(Union::Bool(true, DEFAULT_TAG_VALUE, ReadWrite));
|
||||
pub const TRUE: Self = Self::from_bool(true);
|
||||
/// A [`Dynamic`] containing a [`false`].
|
||||
pub const FALSE: Dynamic = Self(Union::Bool(false, DEFAULT_TAG_VALUE, ReadWrite));
|
||||
pub const FALSE: Self = Self::from_bool(false);
|
||||
/// A [`Dynamic`] containing the integer zero.
|
||||
pub const ZERO: Dynamic = Self(Union::Int(0, DEFAULT_TAG_VALUE, ReadWrite));
|
||||
pub const ZERO: Self = Self::from_int(0);
|
||||
/// A [`Dynamic`] containing the integer one.
|
||||
pub const ONE: Dynamic = Self(Union::Int(1, DEFAULT_TAG_VALUE, ReadWrite));
|
||||
pub const ONE: Self = Self::from_int(1);
|
||||
/// A [`Dynamic`] containing the integer two.
|
||||
pub const TWO: Dynamic = Self(Union::Int(2, DEFAULT_TAG_VALUE, ReadWrite));
|
||||
pub const TWO: Self = Self::from_int(2);
|
||||
/// A [`Dynamic`] containing the integer three.
|
||||
pub const THREE: Self = Self::from_int(3);
|
||||
/// A [`Dynamic`] containing the integer ten.
|
||||
pub const TEN: Dynamic = Self(Union::Int(10, DEFAULT_TAG_VALUE, ReadWrite));
|
||||
pub const TEN: Self = Self::from_int(10);
|
||||
/// A [`Dynamic`] containing the integer one hundred.
|
||||
pub const HUNDRED: Self = Self::from_int(100);
|
||||
/// A [`Dynamic`] containing the integer one thousand.
|
||||
pub const THOUSAND: Self = Self::from_int(1000);
|
||||
/// A [`Dynamic`] containing the integer negative one.
|
||||
pub const NEGATIVE_ONE: Dynamic = Self(Union::Int(-1, DEFAULT_TAG_VALUE, ReadWrite));
|
||||
pub const NEGATIVE_ONE: Self = Self::from_int(-1);
|
||||
/// A [`Dynamic`] containing `0.0`.
|
||||
///
|
||||
/// Not available under `no_float`.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
pub const FLOAT_ZERO: Dynamic = Self(Union::Float(
|
||||
FloatWrapper::new_const(0.0),
|
||||
DEFAULT_TAG_VALUE,
|
||||
ReadWrite,
|
||||
));
|
||||
pub const FLOAT_ZERO: Self = Self::from_float(0.0);
|
||||
/// A [`Dynamic`] containing `1.0`.
|
||||
///
|
||||
/// Not available under `no_float`.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
pub const FLOAT_ONE: Dynamic = Self(Union::Float(
|
||||
FloatWrapper::new_const(1.0),
|
||||
DEFAULT_TAG_VALUE,
|
||||
ReadWrite,
|
||||
));
|
||||
pub const FLOAT_ONE: Self = Self::from_float(1.0);
|
||||
/// A [`Dynamic`] containing `2.0`.
|
||||
///
|
||||
/// Not available under `no_float`.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
pub const FLOAT_TWO: Dynamic = Self(Union::Float(
|
||||
FloatWrapper::new_const(2.0),
|
||||
DEFAULT_TAG_VALUE,
|
||||
ReadWrite,
|
||||
));
|
||||
pub const FLOAT_TWO: Self = Self::from_float(2.0);
|
||||
/// A [`Dynamic`] containing `10.0`.
|
||||
///
|
||||
/// Not available under `no_float`.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
pub const FLOAT_TEN: Dynamic = Self(Union::Float(
|
||||
FloatWrapper::new_const(10.0),
|
||||
DEFAULT_TAG_VALUE,
|
||||
ReadWrite,
|
||||
));
|
||||
/// A [`Dynamic`] containing the `-1.0`.
|
||||
pub const FLOAT_TEN: Self = Self::from_float(10.0);
|
||||
/// A [`Dynamic`] containing `100.0`.
|
||||
///
|
||||
/// Not available under `no_float`.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
pub const FLOAT_NEGATIVE_ONE: Dynamic = Self(Union::Float(
|
||||
FloatWrapper::new_const(-1.0),
|
||||
pub const FLOAT_HUNDRED: Self = Self::from_float(100.0);
|
||||
/// A [`Dynamic`] containing `1000.0`.
|
||||
///
|
||||
/// Not available under `no_float`.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
pub const FLOAT_THOUSAND: Self = Self::from_float(1000.0);
|
||||
/// A [`Dynamic`] containing `-1.0`.
|
||||
///
|
||||
/// Not available under `no_float`.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
pub const FLOAT_NEGATIVE_ONE: Self = Self::from_float(-1.0);
|
||||
/// A [`Dynamic`] containing π.
|
||||
///
|
||||
/// Not available under `no_float`.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[cfg(not(feature = "f32_float"))]
|
||||
pub const FLOAT_PI: Self = Self::from_float(
|
||||
#[cfg(not(feature = "f32_float"))]
|
||||
{
|
||||
std::f64::consts::PI
|
||||
},
|
||||
#[cfg(feature = "f32_float")]
|
||||
{
|
||||
std::f32::consts::PI
|
||||
},
|
||||
);
|
||||
/// A [`Dynamic`] containing π/2.
|
||||
///
|
||||
/// Not available under `no_float`.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
pub const FLOAT_HALF_PI: Self = Self::from_float(
|
||||
#[cfg(not(feature = "f32_float"))]
|
||||
{
|
||||
std::f64::consts::PI / 2.0
|
||||
},
|
||||
#[cfg(feature = "f32_float")]
|
||||
{
|
||||
std::f32::consts::PI / 2.0
|
||||
},
|
||||
);
|
||||
/// A [`Dynamic`] containing 2π.
|
||||
///
|
||||
/// Not available under `no_float`.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
pub const FLOAT_TWO_PI: Self = Self::from_float(
|
||||
#[cfg(not(feature = "f32_float"))]
|
||||
{
|
||||
std::f64::consts::PI * 2.0
|
||||
},
|
||||
#[cfg(feature = "f32_float")]
|
||||
{
|
||||
std::f32::consts::PI * 2.0
|
||||
},
|
||||
);
|
||||
|
||||
/// Create a new [`Dynamic`] from a [`bool`].
|
||||
#[inline(always)]
|
||||
pub const fn from_bool(value: bool) -> Self {
|
||||
Self(Union::Bool(value, DEFAULT_TAG_VALUE, ReadWrite))
|
||||
}
|
||||
/// Create a new [`Dynamic`] from an [`INT`].
|
||||
#[inline(always)]
|
||||
pub const fn from_int(value: INT) -> Self {
|
||||
Self(Union::Int(value, DEFAULT_TAG_VALUE, ReadWrite))
|
||||
}
|
||||
/// Create a new [`Dynamic`] from a [`char`].
|
||||
#[inline(always)]
|
||||
pub const fn from_char(value: char) -> Self {
|
||||
Self(Union::Char(value, DEFAULT_TAG_VALUE, ReadWrite))
|
||||
}
|
||||
/// Create a new [`Dynamic`] from a [`FLOAT`].
|
||||
///
|
||||
/// Not available under `no_float`.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[inline(always)]
|
||||
pub const fn from_float(value: FLOAT) -> Self {
|
||||
Self(Union::Float(
|
||||
FloatWrapper::new_const(value),
|
||||
DEFAULT_TAG_VALUE,
|
||||
ReadWrite,
|
||||
));
|
||||
))
|
||||
}
|
||||
/// Create a new [`Dynamic`] from a [`Decimal`](https://docs.rs/rust_decimal).
|
||||
///
|
||||
/// Exported under the `decimal` feature only.
|
||||
#[cfg(feature = "decimal")]
|
||||
#[inline(always)]
|
||||
pub fn from_decimal(value: Decimal) -> Self {
|
||||
Self(Union::Decimal(value.into(), DEFAULT_TAG_VALUE, ReadWrite))
|
||||
}
|
||||
/// Create a new [`Dynamic`] from an [`Instant`].
|
||||
///
|
||||
/// Not available under `no-std`.
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[inline(always)]
|
||||
pub fn from_timestamp(value: Instant) -> Self {
|
||||
Self(Union::TimeStamp(value.into(), DEFAULT_TAG_VALUE, ReadWrite))
|
||||
}
|
||||
|
||||
/// Get the [`AccessMode`] for this [`Dynamic`].
|
||||
#[must_use]
|
||||
@ -1816,14 +1900,15 @@ impl Dynamic {
|
||||
/// Convert the [`Dynamic`] into a [`String`] and return it.
|
||||
/// If there are other references to the same string, a cloned copy is returned.
|
||||
/// Returns the name of the actual type if the cast fails.
|
||||
#[inline]
|
||||
pub fn as_string(self) -> Result<String, &'static str> {
|
||||
self.as_immutable_string().map(ImmutableString::into_owned)
|
||||
#[inline(always)]
|
||||
pub fn into_string(self) -> Result<String, &'static str> {
|
||||
self.into_immutable_string()
|
||||
.map(ImmutableString::into_owned)
|
||||
}
|
||||
/// Convert the [`Dynamic`] into an [`ImmutableString`] and return it.
|
||||
/// Returns the name of the actual type if the cast fails.
|
||||
#[inline]
|
||||
pub fn as_immutable_string(self) -> Result<ImmutableString, &'static str> {
|
||||
pub fn into_immutable_string(self) -> Result<ImmutableString, &'static str> {
|
||||
match self.0 {
|
||||
Union::Str(s, _, _) => Ok(s),
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
@ -1900,6 +1985,13 @@ impl From<&ImmutableString> for Dynamic {
|
||||
value.clone().into()
|
||||
}
|
||||
}
|
||||
impl FromStr for Dynamic {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(value: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Self(Union::Str(value.into(), DEFAULT_TAG_VALUE, ReadWrite)))
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
impl Dynamic {
|
||||
/// Create a [`Dynamic`] from an [`Array`].
|
||||
|
@ -1357,9 +1357,9 @@ impl Engine {
|
||||
|
||||
// Trims the JSON string and add a '#' in front
|
||||
let json_text = json.trim_start();
|
||||
let scripts = if json_text.starts_with(Token::MapStart.keyword_syntax()) {
|
||||
let scripts = if json_text.starts_with(Token::MapStart.literal_syntax()) {
|
||||
[json_text, ""]
|
||||
} else if json_text.starts_with(Token::LeftBrace.keyword_syntax()) {
|
||||
} else if json_text.starts_with(Token::LeftBrace.literal_syntax()) {
|
||||
["#", json_text]
|
||||
} else {
|
||||
return Err(crate::ParseErrorType::MissingToken(
|
||||
@ -1741,40 +1741,36 @@ impl Engine {
|
||||
let lib = &[ast.lib()];
|
||||
self.eval_global_statements(scope, mods, &mut state, statements, lib, level)
|
||||
}
|
||||
/// Evaluate a file, but throw away the result and only return error (if any).
|
||||
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
||||
/// Evaluate a file, returning any error (if any).
|
||||
///
|
||||
/// Not available under `no_std` or `WASM`.
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
||||
#[inline(always)]
|
||||
pub fn consume_file(&self, path: std::path::PathBuf) -> Result<(), Box<EvalAltResult>> {
|
||||
Self::read_file(path).and_then(|contents| self.consume(&contents))
|
||||
pub fn run_file(&self, path: std::path::PathBuf) -> Result<(), Box<EvalAltResult>> {
|
||||
Self::read_file(path).and_then(|contents| self.run(&contents))
|
||||
}
|
||||
/// Evaluate a file with own scope, but throw away the result and only return error (if any).
|
||||
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
||||
/// Evaluate a file with own scope, returning any error (if any).
|
||||
///
|
||||
/// Not available under `no_std` or `WASM`.
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
||||
#[inline(always)]
|
||||
pub fn consume_file_with_scope(
|
||||
pub fn run_file_with_scope(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
path: std::path::PathBuf,
|
||||
) -> Result<(), Box<EvalAltResult>> {
|
||||
Self::read_file(path).and_then(|contents| self.consume_with_scope(scope, &contents))
|
||||
Self::read_file(path).and_then(|contents| self.run_with_scope(scope, &contents))
|
||||
}
|
||||
/// Evaluate a string, but throw away the result and only return error (if any).
|
||||
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
||||
/// Evaluate a script, returning any error (if any).
|
||||
#[inline(always)]
|
||||
pub fn consume(&self, script: &str) -> Result<(), Box<EvalAltResult>> {
|
||||
self.consume_with_scope(&mut Default::default(), script)
|
||||
pub fn run(&self, script: &str) -> Result<(), Box<EvalAltResult>> {
|
||||
self.run_with_scope(&mut Default::default(), script)
|
||||
}
|
||||
/// Evaluate a string with own scope, but throw away the result and only return error (if any).
|
||||
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
||||
/// Evaluate a script with own scope, returning any error (if any).
|
||||
#[inline]
|
||||
pub fn consume_with_scope(
|
||||
pub fn run_with_scope(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
script: &str,
|
||||
@ -1790,18 +1786,16 @@ impl Engine {
|
||||
self.optimization_level,
|
||||
)?;
|
||||
|
||||
self.consume_ast_with_scope(scope, &ast)
|
||||
self.run_ast_with_scope(scope, &ast)
|
||||
}
|
||||
/// Evaluate an AST, but throw away the result and only return error (if any).
|
||||
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
||||
/// Evaluate an AST, returning any error (if any).
|
||||
#[inline(always)]
|
||||
pub fn consume_ast(&self, ast: &AST) -> Result<(), Box<EvalAltResult>> {
|
||||
self.consume_ast_with_scope(&mut Default::default(), ast)
|
||||
pub fn run_ast(&self, ast: &AST) -> Result<(), Box<EvalAltResult>> {
|
||||
self.run_ast_with_scope(&mut Default::default(), ast)
|
||||
}
|
||||
/// Evaluate an [`AST`] with own scope, but throw away the result and only return error (if any).
|
||||
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
||||
/// Evaluate an [`AST`] with own scope, returning any error (if any).
|
||||
#[inline]
|
||||
pub fn consume_ast_with_scope(
|
||||
pub fn run_ast_with_scope(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
ast: &AST,
|
||||
@ -1985,7 +1979,10 @@ impl Engine {
|
||||
let statements = ast.statements();
|
||||
|
||||
if eval_ast && !statements.is_empty() {
|
||||
// Make sure new variables introduced at global level do not _spill_ into the function call
|
||||
let orig_scope_len = scope.len();
|
||||
self.eval_global_statements(scope, mods, state, statements, lib, 0)?;
|
||||
scope.rewind(orig_scope_len);
|
||||
}
|
||||
|
||||
let fn_def = ast
|
||||
@ -2151,7 +2148,7 @@ impl Engine {
|
||||
/// }
|
||||
/// });
|
||||
///
|
||||
/// engine.consume("for x in range(0, 50000) {}")
|
||||
/// engine.run("for x in range(0, 50000) {}")
|
||||
/// .expect_err("should error");
|
||||
///
|
||||
/// assert_eq!(*result.read().unwrap(), 9600);
|
||||
@ -2186,7 +2183,7 @@ impl Engine {
|
||||
/// let logger = result.clone();
|
||||
/// engine.on_print(move |s| logger.write().unwrap().push_str(s));
|
||||
///
|
||||
/// engine.consume("print(40 + 2);")?;
|
||||
/// engine.run("print(40 + 2);")?;
|
||||
///
|
||||
/// assert_eq!(*result.read().unwrap(), "42");
|
||||
/// # Ok(())
|
||||
@ -2219,7 +2216,7 @@ impl Engine {
|
||||
///
|
||||
/// let mut ast = engine.compile(r#"let x = "hello"; debug(x);"#)?;
|
||||
/// ast.set_source("world");
|
||||
/// engine.consume_ast(&ast)?;
|
||||
/// engine.run_ast(&ast)?;
|
||||
///
|
||||
/// #[cfg(not(feature = "no_position"))]
|
||||
/// assert_eq!(*result.read().unwrap(), r#"world @ 1:18 > "hello""#);
|
||||
|
@ -307,13 +307,13 @@ impl Engine {
|
||||
None | Some(Token::Reserved(_)) | Some(Token::Custom(_)) => (),
|
||||
// Active standard keywords cannot be made custom
|
||||
// Disabled keywords are OK
|
||||
Some(token) if token.is_keyword() => {
|
||||
Some(token) if token.is_standard_keyword() => {
|
||||
if !self.disabled_symbols.contains(token.syntax().as_ref()) {
|
||||
return Err(format!("'{}' is a reserved keyword", keyword.as_ref()));
|
||||
}
|
||||
}
|
||||
// Active standard symbols cannot be made custom
|
||||
Some(token) if token.is_symbol() => {
|
||||
Some(token) if token.is_standard_symbol() => {
|
||||
if !self.disabled_symbols.contains(token.syntax().as_ref()) {
|
||||
return Err(format!("'{}' is a reserved operator", keyword.as_ref()));
|
||||
}
|
||||
|
@ -235,6 +235,8 @@ impl fmt::Display for ParseErrorType {
|
||||
Self::MismatchedType(r, a) => write!(f, "Expecting {}, not {}", r, a),
|
||||
Self::ExprExpected(s) => write!(f, "Expecting {} expression", s),
|
||||
Self::MissingToken(token, s) => write!(f, "Expecting '{}' {}", token, s),
|
||||
|
||||
Self::MissingSymbol(s) if s.is_empty() => f.write_str("Expecting a symbol"),
|
||||
Self::MissingSymbol(s) => f.write_str(s),
|
||||
|
||||
Self::AssignmentToConstant(s) => match s.as_str() {
|
||||
|
@ -321,7 +321,7 @@ impl Engine {
|
||||
return Ok(match name {
|
||||
KEYWORD_PRINT => {
|
||||
if let Some(ref print) = self.print {
|
||||
let text = result.as_immutable_string().map_err(|typ| {
|
||||
let text = result.into_immutable_string().map_err(|typ| {
|
||||
EvalAltResult::ErrorMismatchOutputType(
|
||||
self.map_type_name(type_name::<ImmutableString>()).into(),
|
||||
typ.into(),
|
||||
@ -335,7 +335,7 @@ impl Engine {
|
||||
}
|
||||
KEYWORD_DEBUG => {
|
||||
if let Some(ref debug) = self.debug {
|
||||
let text = result.as_immutable_string().map_err(|typ| {
|
||||
let text = result.into_immutable_string().map_err(|typ| {
|
||||
EvalAltResult::ErrorMismatchOutputType(
|
||||
self.map_type_name(type_name::<ImmutableString>()).into(),
|
||||
typ.into(),
|
||||
@ -1100,7 +1100,7 @@ impl Engine {
|
||||
|
||||
// Fn - only in function call style
|
||||
return arg
|
||||
.as_immutable_string()
|
||||
.into_immutable_string()
|
||||
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))
|
||||
.and_then(FnPtr::try_from)
|
||||
.map(Into::<Dynamic>::into)
|
||||
@ -1150,7 +1150,7 @@ impl Engine {
|
||||
)?;
|
||||
|
||||
let fn_name = arg
|
||||
.as_immutable_string()
|
||||
.into_immutable_string()
|
||||
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))?;
|
||||
|
||||
let (arg, arg_pos) = self.get_arg_value(
|
||||
@ -1176,7 +1176,7 @@ impl Engine {
|
||||
scope, mods, state, lib, this_ptr, level, args_expr, constants, 0,
|
||||
)?;
|
||||
let var_name = arg
|
||||
.as_immutable_string()
|
||||
.into_immutable_string()
|
||||
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))?;
|
||||
return Ok(scope.contains(&var_name).into());
|
||||
}
|
||||
@ -1189,7 +1189,7 @@ impl Engine {
|
||||
scope, mods, state, lib, this_ptr, level, args_expr, constants, 0,
|
||||
)?;
|
||||
let script = &value
|
||||
.as_immutable_string()
|
||||
.into_immutable_string()
|
||||
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, pos))?;
|
||||
let result =
|
||||
self.eval_script_expr_in_place(scope, mods, state, lib, script, pos, level + 1);
|
||||
|
@ -53,7 +53,7 @@ pub fn by_value<T: Variant + Clone>(data: &mut Dynamic) -> T {
|
||||
} else if TypeId::of::<T>() == TypeId::of::<String>() {
|
||||
// If T is `String`, data must be `ImmutableString`, so map directly to it
|
||||
let value = mem::take(data)
|
||||
.as_string()
|
||||
.into_string()
|
||||
.expect("never fails because the type was checked");
|
||||
unsafe_try_cast(value).expect("never fails because the type was checked")
|
||||
} else {
|
||||
|
@ -71,6 +71,7 @@ use std::prelude::v1::*;
|
||||
|
||||
mod ast;
|
||||
mod custom_syntax;
|
||||
mod deprecated;
|
||||
mod dynamic;
|
||||
mod engine;
|
||||
mod engine_api;
|
||||
|
@ -309,6 +309,22 @@ mod decimal_functions {
|
||||
Decimal, MathematicalOps,
|
||||
};
|
||||
|
||||
#[cfg(feature = "no_float")]
|
||||
pub mod float_polyfills {
|
||||
#[rhai_fn(name = "PI")]
|
||||
pub fn pi() -> Decimal {
|
||||
Decimal::PI
|
||||
}
|
||||
#[rhai_fn(name = "E")]
|
||||
pub fn e() -> Decimal {
|
||||
Decimal::E
|
||||
}
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn parse_float(s: &str) -> Result<Decimal, Box<EvalAltResult>> {
|
||||
super::parse_decimal(s)
|
||||
}
|
||||
}
|
||||
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn sqrt(x: Decimal) -> Result<Decimal, Box<EvalAltResult>> {
|
||||
x.sqrt()
|
||||
@ -323,8 +339,23 @@ mod decimal_functions {
|
||||
Ok(x.exp())
|
||||
}
|
||||
}
|
||||
pub fn ln(x: Decimal) -> Decimal {
|
||||
x.ln()
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn ln(x: Decimal) -> Result<Decimal, Box<EvalAltResult>> {
|
||||
if cfg!(not(feature = "unchecked")) {
|
||||
x.checked_ln()
|
||||
.ok_or_else(|| make_err(format!("Error taking the natural log of {}", x)))
|
||||
} else {
|
||||
Ok(x.ln())
|
||||
}
|
||||
}
|
||||
#[rhai_fn(name = "log", return_raw)]
|
||||
pub fn log10(x: Decimal) -> Result<Decimal, Box<EvalAltResult>> {
|
||||
if cfg!(not(feature = "unchecked")) {
|
||||
x.checked_log10()
|
||||
.ok_or_else(|| make_err(format!("Error taking the log of {}", x)))
|
||||
} else {
|
||||
Ok(x.log10())
|
||||
}
|
||||
}
|
||||
#[rhai_fn(name = "floor", get = "floor")]
|
||||
pub fn floor(x: Decimal) -> Decimal {
|
||||
|
@ -30,7 +30,7 @@ pub fn print_with_func(
|
||||
) -> crate::ImmutableString {
|
||||
match ctx.call_fn_dynamic_raw(fn_name, true, &mut [value]) {
|
||||
Ok(result) if result.is::<crate::ImmutableString>() => result
|
||||
.as_immutable_string()
|
||||
.into_immutable_string()
|
||||
.expect("never fails as the result is `ImmutableString`"),
|
||||
Ok(result) => ctx.engine().map_type_name(result.type_name()).into(),
|
||||
Err(_) => ctx.engine().map_type_name(value.type_name()).into(),
|
||||
|
84
src/parse.rs
84
src/parse.rs
@ -4,10 +4,7 @@ use crate::ast::{
|
||||
BinaryExpr, CustomExpr, Expr, FnCallExpr, FnCallHashes, Ident, OpAssignment, ReturnType,
|
||||
ScriptFnDef, Stmt, StmtBlock, AST_OPTION_FLAGS::*,
|
||||
};
|
||||
use crate::custom_syntax::{
|
||||
CustomSyntax, CUSTOM_SYNTAX_MARKER_BLOCK, CUSTOM_SYNTAX_MARKER_BOOL, CUSTOM_SYNTAX_MARKER_EXPR,
|
||||
CUSTOM_SYNTAX_MARKER_IDENT, CUSTOM_SYNTAX_MARKER_INT, CUSTOM_SYNTAX_MARKER_STRING,
|
||||
};
|
||||
use crate::custom_syntax::{markers::*, CustomSyntax};
|
||||
use crate::dynamic::AccessMode;
|
||||
use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS};
|
||||
use crate::fn_hash::get_hasher;
|
||||
@ -17,8 +14,8 @@ use crate::token::{
|
||||
is_keyword_function, is_valid_identifier, Token, TokenStream, TokenizerControl,
|
||||
};
|
||||
use crate::{
|
||||
calc_fn_hash, calc_qualified_fn_hash, calc_qualified_var_hash, Engine, Identifier, LexError,
|
||||
ParseError, ParseErrorType, Position, Scope, Shared, StaticVec, AST,
|
||||
calc_fn_hash, calc_qualified_fn_hash, calc_qualified_var_hash, Engine, Identifier,
|
||||
ImmutableString, LexError, ParseError, ParseErrorType, Position, Scope, Shared, StaticVec, AST,
|
||||
};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
@ -29,7 +26,7 @@ use std::{
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use crate::{custom_syntax::CUSTOM_SYNTAX_MARKER_FLOAT, FLOAT};
|
||||
use crate::{custom_syntax::markers::CUSTOM_SYNTAX_MARKER_FLOAT, FLOAT};
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
use crate::FnAccess;
|
||||
@ -389,6 +386,20 @@ fn parse_var_name(input: &mut TokenStream) -> Result<(String, Position), ParseEr
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a symbol.
|
||||
fn parse_symbol(input: &mut TokenStream) -> Result<(String, Position), ParseError> {
|
||||
match input.next().expect(NEVER_ENDS) {
|
||||
// Symbol
|
||||
(token, pos) if token.is_standard_symbol() => Ok((token.literal_syntax().into(), pos)),
|
||||
// Reserved symbol
|
||||
(Token::Reserved(s), pos) if !is_valid_identifier(s.chars()) => Ok((s, pos)),
|
||||
// Bad identifier
|
||||
(Token::LexError(err), pos) => Err(err.into_err(pos)),
|
||||
// Not a symbol
|
||||
(_, pos) => Err(PERR::MissingSymbol(Default::default()).into_err(pos)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse `(` expr `)`
|
||||
fn parse_paren_expr(
|
||||
input: &mut TokenStream,
|
||||
@ -1672,15 +1683,9 @@ fn make_dot_expr(
|
||||
op_pos: Position,
|
||||
) -> Result<Expr, ParseError> {
|
||||
Ok(match (lhs, rhs) {
|
||||
// lhs[???]...[???].rhs
|
||||
(Expr::Index(mut x, false, pos), rhs) if !terminate_chaining => {
|
||||
// Attach dot chain to the bottom level of indexing chain
|
||||
x.rhs = make_dot_expr(state, x.rhs, false, rhs, op_pos)?;
|
||||
Expr::Index(x, false, pos)
|
||||
}
|
||||
// lhs[idx_expr].rhs
|
||||
(Expr::Index(mut x, _, pos), rhs) => {
|
||||
x.rhs = make_dot_expr(state, x.rhs, true, rhs, op_pos)?;
|
||||
(Expr::Index(mut x, term, pos), rhs) => {
|
||||
x.rhs = make_dot_expr(state, x.rhs, term || terminate_chaining, rhs, op_pos)?;
|
||||
Expr::Index(x, false, pos)
|
||||
}
|
||||
// lhs.id
|
||||
@ -1993,16 +1998,17 @@ fn parse_custom_syntax(
|
||||
}
|
||||
|
||||
let parse_func = syntax.parse.as_ref();
|
||||
let mut required_token: ImmutableString = key.into();
|
||||
|
||||
segments.push(key.into());
|
||||
tokens.push(key.into());
|
||||
tokens.push(required_token.clone().into());
|
||||
segments.push(required_token.clone());
|
||||
|
||||
loop {
|
||||
let (fwd_token, fwd_pos) = input.peek().expect(NEVER_ENDS);
|
||||
settings.pos = *fwd_pos;
|
||||
let settings = settings.level_up();
|
||||
|
||||
let required_token = match parse_func(&segments, fwd_token.syntax().as_ref()) {
|
||||
required_token = match parse_func(&segments, fwd_token.syntax().as_ref()) {
|
||||
Ok(Some(seg)) => seg,
|
||||
Ok(None) => break,
|
||||
Err(err) => return Err(err.0.into_err(settings.pos)),
|
||||
@ -2016,6 +2022,13 @@ fn parse_custom_syntax(
|
||||
tokens.push(state.get_identifier(CUSTOM_SYNTAX_MARKER_IDENT));
|
||||
keywords.push(Expr::Variable(None, pos, (None, None, name).into()));
|
||||
}
|
||||
CUSTOM_SYNTAX_MARKER_SYMBOL => {
|
||||
let (symbol, pos) = parse_symbol(input)?;
|
||||
let symbol: ImmutableString = state.get_identifier(symbol).into();
|
||||
segments.push(symbol.clone());
|
||||
tokens.push(state.get_identifier(CUSTOM_SYNTAX_MARKER_SYMBOL));
|
||||
keywords.push(Expr::StringConstant(symbol, pos));
|
||||
}
|
||||
CUSTOM_SYNTAX_MARKER_EXPR => {
|
||||
keywords.push(parse_expr(input, state, lib, settings)?);
|
||||
let keyword = state.get_identifier(CUSTOM_SYNTAX_MARKER_EXPR);
|
||||
@ -2034,9 +2047,8 @@ fn parse_custom_syntax(
|
||||
CUSTOM_SYNTAX_MARKER_BOOL => match input.next().expect(NEVER_ENDS) {
|
||||
(b @ Token::True, pos) | (b @ Token::False, pos) => {
|
||||
keywords.push(Expr::BoolConstant(b == Token::True, pos));
|
||||
let keyword = state.get_identifier(CUSTOM_SYNTAX_MARKER_BOOL);
|
||||
segments.push(keyword.clone().into());
|
||||
tokens.push(keyword);
|
||||
segments.push(state.get_identifier(b.literal_syntax()).into());
|
||||
tokens.push(state.get_identifier(CUSTOM_SYNTAX_MARKER_BOOL));
|
||||
}
|
||||
(_, pos) => {
|
||||
return Err(
|
||||
@ -2048,9 +2060,8 @@ fn parse_custom_syntax(
|
||||
CUSTOM_SYNTAX_MARKER_INT => match input.next().expect(NEVER_ENDS) {
|
||||
(Token::IntegerConstant(i), pos) => {
|
||||
keywords.push(Expr::IntegerConstant(i, pos));
|
||||
let keyword = state.get_identifier(CUSTOM_SYNTAX_MARKER_INT);
|
||||
segments.push(keyword.clone().into());
|
||||
tokens.push(keyword);
|
||||
segments.push(i.to_string().into());
|
||||
tokens.push(state.get_identifier(CUSTOM_SYNTAX_MARKER_INT));
|
||||
}
|
||||
(_, pos) => {
|
||||
return Err(
|
||||
@ -2063,9 +2074,8 @@ fn parse_custom_syntax(
|
||||
CUSTOM_SYNTAX_MARKER_FLOAT => match input.next().expect(NEVER_ENDS) {
|
||||
(Token::FloatConstant(f), pos) => {
|
||||
keywords.push(Expr::FloatConstant(f, pos));
|
||||
let keyword = state.get_identifier(CUSTOM_SYNTAX_MARKER_FLOAT);
|
||||
segments.push(keyword.clone().into());
|
||||
tokens.push(keyword);
|
||||
segments.push(f.to_string().into());
|
||||
tokens.push(state.get_identifier(CUSTOM_SYNTAX_MARKER_FLOAT));
|
||||
}
|
||||
(_, pos) => {
|
||||
return Err(PERR::MissingSymbol(
|
||||
@ -2076,10 +2086,10 @@ fn parse_custom_syntax(
|
||||
},
|
||||
CUSTOM_SYNTAX_MARKER_STRING => match input.next().expect(NEVER_ENDS) {
|
||||
(Token::StringConstant(s), pos) => {
|
||||
keywords.push(Expr::StringConstant(state.get_identifier(s).into(), pos));
|
||||
let keyword = state.get_identifier(CUSTOM_SYNTAX_MARKER_STRING);
|
||||
segments.push(keyword.clone().into());
|
||||
tokens.push(keyword);
|
||||
let s: ImmutableString = state.get_identifier(s).into();
|
||||
keywords.push(Expr::StringConstant(s.clone(), pos));
|
||||
segments.push(s);
|
||||
tokens.push(state.get_identifier(CUSTOM_SYNTAX_MARKER_STRING));
|
||||
}
|
||||
(_, pos) => {
|
||||
return Err(PERR::MissingSymbol("Expecting a string".to_string()).into_err(pos))
|
||||
@ -2105,11 +2115,23 @@ fn parse_custom_syntax(
|
||||
keywords.shrink_to_fit();
|
||||
tokens.shrink_to_fit();
|
||||
|
||||
const KEYWORD_SEMICOLON: &str = Token::SemiColon.literal_syntax();
|
||||
const KEYWORD_CLOSE_BRACE: &str = Token::RightBrace.literal_syntax();
|
||||
|
||||
let self_terminated = match required_token.as_str() {
|
||||
// It is self-terminating if the last symbol is a block
|
||||
CUSTOM_SYNTAX_MARKER_BLOCK => true,
|
||||
// If the last symbol is `;` or `}`, it is self-terminating
|
||||
KEYWORD_SEMICOLON | KEYWORD_CLOSE_BRACE => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
Ok(Expr::Custom(
|
||||
CustomExpr {
|
||||
keywords,
|
||||
tokens,
|
||||
scope_may_be_changed: syntax.scope_may_be_changed,
|
||||
self_terminated,
|
||||
}
|
||||
.into(),
|
||||
pos,
|
||||
|
79
src/scope.rs
79
src/scope.rs
@ -339,6 +339,75 @@ impl<'a> Scope<'a> {
|
||||
.find(|(_, (key, _))| name == key.as_ref())
|
||||
.and_then(|(index, _)| self.values[index].flatten_clone().try_cast())
|
||||
}
|
||||
/// Check if the named entry in the [`Scope`] is constant.
|
||||
///
|
||||
/// Search starts backwards from the last, stopping at the first entry matching the specified name.
|
||||
///
|
||||
/// Returns [`None`] if no entry matching the specified name is found.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Scope;
|
||||
///
|
||||
/// let mut my_scope = Scope::new();
|
||||
///
|
||||
/// my_scope.push_constant("x", 42_i64);
|
||||
/// assert_eq!(my_scope.is_constant("x"), Some(true));
|
||||
/// assert_eq!(my_scope.is_constant("y"), None);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn is_constant(&self, name: &str) -> Option<bool> {
|
||||
self.get_index(name).and_then(|(_, access)| match access {
|
||||
AccessMode::ReadWrite => None,
|
||||
AccessMode::ReadOnly => Some(true),
|
||||
})
|
||||
}
|
||||
/// Update the value of the named entry in the [`Scope`] if it already exists and is not constant.
|
||||
/// Push a new entry with the value into the [`Scope`] if the name doesn't exist or if the
|
||||
/// existing entry is constant.
|
||||
///
|
||||
/// Search starts backwards from the last, and only the first entry matching the specified name is updated.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Scope;
|
||||
///
|
||||
/// let mut my_scope = Scope::new();
|
||||
///
|
||||
/// my_scope.set_or_push("x", 42_i64);
|
||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
|
||||
/// assert_eq!(my_scope.len(), 1);
|
||||
///
|
||||
/// my_scope.set_or_push("x", 0_i64);
|
||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 0);
|
||||
/// assert_eq!(my_scope.len(), 1);
|
||||
///
|
||||
/// my_scope.set_or_push("y", 123_i64);
|
||||
/// assert_eq!(my_scope.get_value::<i64>("y").unwrap(), 123);
|
||||
/// assert_eq!(my_scope.len(), 2);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn set_or_push(
|
||||
&mut self,
|
||||
name: impl AsRef<str> + Into<Cow<'a, str>>,
|
||||
value: impl Variant + Clone,
|
||||
) -> &mut Self {
|
||||
match self.get_index(name.as_ref()) {
|
||||
None | Some((_, AccessMode::ReadOnly)) => {
|
||||
self.push(name, value);
|
||||
}
|
||||
Some((index, AccessMode::ReadWrite)) => {
|
||||
let value_ref = self
|
||||
.values
|
||||
.get_mut(index)
|
||||
.expect("never fails because the index is returned by `get_index`");
|
||||
*value_ref = Dynamic::from(value);
|
||||
}
|
||||
}
|
||||
self
|
||||
}
|
||||
/// Update the value of the named entry in the [`Scope`].
|
||||
///
|
||||
/// Search starts backwards from the last, and only the first entry matching the specified name is updated.
|
||||
@ -362,12 +431,16 @@ impl<'a> Scope<'a> {
|
||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 0);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn set_value(&mut self, name: &'a str, value: impl Variant + Clone) -> &mut Self {
|
||||
match self.get_index(name) {
|
||||
pub fn set_value(
|
||||
&mut self,
|
||||
name: impl AsRef<str> + Into<Cow<'a, str>>,
|
||||
value: impl Variant + Clone,
|
||||
) -> &mut Self {
|
||||
match self.get_index(name.as_ref()) {
|
||||
None => {
|
||||
self.push(name, value);
|
||||
}
|
||||
Some((_, AccessMode::ReadOnly)) => panic!("variable {} is constant", name),
|
||||
Some((_, AccessMode::ReadOnly)) => panic!("variable {} is constant", name.as_ref()),
|
||||
Some((index, AccessMode::ReadWrite)) => {
|
||||
let value_ref = self
|
||||
.values
|
||||
|
@ -547,7 +547,7 @@ impl SerializeMap for DynamicSerializer {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
{
|
||||
let key = std::mem::take(&mut self._key)
|
||||
.as_immutable_string()
|
||||
.into_immutable_string()
|
||||
.map_err(|typ| {
|
||||
EvalAltResult::ErrorMismatchDataType(
|
||||
"string".into(),
|
||||
@ -577,7 +577,7 @@ impl SerializeMap for DynamicSerializer {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
{
|
||||
let _key: Dynamic = _key.serialize(&mut *self)?;
|
||||
let _key = _key.as_immutable_string().map_err(|typ| {
|
||||
let _key = _key.into_immutable_string().map_err(|typ| {
|
||||
EvalAltResult::ErrorMismatchDataType("string".into(), typ.into(), Position::NONE)
|
||||
})?;
|
||||
let _value = _value.serialize(&mut *self)?;
|
||||
|
14
src/token.rs
14
src/token.rs
@ -483,9 +483,9 @@ pub enum Token {
|
||||
}
|
||||
|
||||
impl Token {
|
||||
/// Get the syntax of the token if it is a keyword.
|
||||
/// Get the literal syntax of the token.
|
||||
#[must_use]
|
||||
pub const fn keyword_syntax(&self) -> &'static str {
|
||||
pub const fn literal_syntax(&self) -> &'static str {
|
||||
use Token::*;
|
||||
|
||||
match self {
|
||||
@ -595,7 +595,7 @@ impl Token {
|
||||
|
||||
EOF => "{EOF}".into(),
|
||||
|
||||
token => token.keyword_syntax().into(),
|
||||
token => token.literal_syntax().into(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -912,7 +912,7 @@ impl Token {
|
||||
|
||||
/// Is this token a standard symbol used in the language?
|
||||
#[must_use]
|
||||
pub const fn is_symbol(&self) -> bool {
|
||||
pub const fn is_standard_symbol(&self) -> bool {
|
||||
use Token::*;
|
||||
|
||||
match self {
|
||||
@ -928,10 +928,10 @@ impl Token {
|
||||
}
|
||||
}
|
||||
|
||||
/// Is this token an active standard keyword?
|
||||
/// Is this token a standard keyword?
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn is_keyword(&self) -> bool {
|
||||
pub const fn is_standard_keyword(&self) -> bool {
|
||||
use Token::*;
|
||||
|
||||
match self {
|
||||
@ -948,7 +948,7 @@ impl Token {
|
||||
}
|
||||
}
|
||||
|
||||
/// Is this token a reserved symbol?
|
||||
/// Is this token a reserved keyword or symbol?
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub const fn is_reserved(&self) -> bool {
|
||||
|
@ -343,7 +343,7 @@ fn test_closures_external() -> Result<(), Box<EvalAltResult>> {
|
||||
// Closure 'f' captures: the engine, the AST, and the curried function pointer
|
||||
let f = move |x: INT| fn_ptr.call_dynamic(&context, None, [x.into()]);
|
||||
|
||||
assert_eq!(f(42)?.as_string(), Ok("hello42".to_string()));
|
||||
assert_eq!(f(42)?.into_string(), Ok("hello42".to_string()));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ fn test_constant() -> Result<(), Box<EvalAltResult>> {
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
assert!(matches!(
|
||||
*engine.consume("const x = [1, 2, 3, 4, 5]; x[2] = 42;").expect_err("expects error"),
|
||||
*engine.run("const x = [1, 2, 3, 4, 5]; x[2] = 42;").expect_err("expects error"),
|
||||
EvalAltResult::ErrorAssignmentToConstant(x, _) if x == "x"
|
||||
));
|
||||
|
||||
@ -30,7 +30,7 @@ fn test_constant_scope() -> Result<(), Box<EvalAltResult>> {
|
||||
scope.push_constant("x", 42 as INT);
|
||||
|
||||
assert!(matches!(
|
||||
*engine.consume_with_scope(&mut scope, "x = 1").expect_err("expects error"),
|
||||
*engine.run_with_scope(&mut scope, "x = 1").expect_err("expects error"),
|
||||
EvalAltResult::ErrorAssignmentToConstant(x, _) if x == "x"
|
||||
));
|
||||
|
||||
@ -80,7 +80,7 @@ fn test_constant_mut() -> Result<(), Box<EvalAltResult>> {
|
||||
|
||||
assert!(matches!(
|
||||
*engine
|
||||
.consume(
|
||||
.run(
|
||||
"
|
||||
const MY_NUMBER = new_ts();
|
||||
MY_NUMBER.value = 42;
|
||||
@ -118,7 +118,7 @@ fn test_constant_mut() -> Result<(), Box<EvalAltResult>> {
|
||||
|
||||
assert!(matches!(
|
||||
*engine
|
||||
.consume_with_scope(&mut scope, "MY_NUMBER.value = 42;")
|
||||
.run_with_scope(&mut scope, "MY_NUMBER.value = 42;")
|
||||
.expect_err("should error"),
|
||||
EvalAltResult::ErrorAssignmentToConstant(_, _)
|
||||
));
|
||||
|
@ -1,10 +1,12 @@
|
||||
use rhai::{Dynamic, Engine, EvalAltResult, LexError, ParseErrorType, Position, INT};
|
||||
use rhai::{
|
||||
Dynamic, Engine, EvalAltResult, ImmutableString, LexError, ParseErrorType, Position, Scope, INT,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
||||
let mut engine = Engine::new();
|
||||
|
||||
engine.consume("while false {}")?;
|
||||
engine.run("while false {}")?;
|
||||
|
||||
// Disable 'while' and make sure it still works with custom syntax
|
||||
engine.disable_symbol("while");
|
||||
@ -19,21 +21,32 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
||||
|
||||
engine.register_custom_syntax(
|
||||
&[
|
||||
"exec", "[", "$ident$", ";", "$int$", "]", "->", "$block$", "while", "$expr$",
|
||||
"exec", "[", "$ident$", "$symbol$", "$int$", "]", "->", "$block$", "while", "$expr$",
|
||||
],
|
||||
true,
|
||||
|context, inputs| {
|
||||
let var_name = inputs[0].get_variable_name().unwrap().to_string();
|
||||
let max = inputs[1].get_literal_value::<INT>().unwrap();
|
||||
let stmt = &inputs[2];
|
||||
let condition = &inputs[3];
|
||||
let op = inputs[1].get_literal_value::<ImmutableString>().unwrap();
|
||||
let max = inputs[2].get_literal_value::<INT>().unwrap();
|
||||
let stmt = &inputs[3];
|
||||
let condition = &inputs[4];
|
||||
|
||||
context.scope_mut().push(var_name.clone(), 0 as INT);
|
||||
|
||||
let mut count: INT = 0;
|
||||
|
||||
loop {
|
||||
if count >= max {
|
||||
let done = match op.as_str() {
|
||||
"<" => count >= max,
|
||||
"<=" => count > max,
|
||||
">" => count <= max,
|
||||
">=" => count < max,
|
||||
"==" => count != max,
|
||||
"!=" => count == max,
|
||||
_ => return Err(format!("Unsupported operator: {}", op).into()),
|
||||
};
|
||||
|
||||
if done {
|
||||
break;
|
||||
}
|
||||
|
||||
@ -64,11 +77,18 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
||||
},
|
||||
)?;
|
||||
|
||||
assert!(matches!(
|
||||
*engine
|
||||
.run("let foo = (exec [x<<15] -> { x += 2 } while x < 42) * 10;")
|
||||
.expect_err("should error"),
|
||||
EvalAltResult::ErrorRuntime(_, _)
|
||||
));
|
||||
|
||||
assert_eq!(
|
||||
engine.eval::<INT>(
|
||||
"
|
||||
let x = 0;
|
||||
let foo = (exec [x;15] -> { x += 2 } while x < 42) * 10;
|
||||
let foo = (exec [x<15] -> { x += 2 } while x < 42) * 10;
|
||||
foo
|
||||
"
|
||||
)?,
|
||||
@ -78,7 +98,7 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
||||
engine.eval::<INT>(
|
||||
"
|
||||
let x = 0;
|
||||
exec [x;100] -> { x += 1 } while x < 42;
|
||||
exec [x<100] -> { x += 1 } while x < 42;
|
||||
x
|
||||
"
|
||||
)?,
|
||||
@ -87,7 +107,7 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
||||
assert_eq!(
|
||||
engine.eval::<INT>(
|
||||
"
|
||||
exec [x;100] -> { x += 1 } while x < 42;
|
||||
exec [x<100] -> { x += 1 } while x < 42;
|
||||
x
|
||||
"
|
||||
)?,
|
||||
@ -97,7 +117,7 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
||||
engine.eval::<INT>(
|
||||
"
|
||||
let foo = 123;
|
||||
exec [x;15] -> { x += 1 } while x < 42;
|
||||
exec [x<15] -> { x += 1 } while x < 42;
|
||||
foo + x + x1 + x2 + x3
|
||||
"
|
||||
)?,
|
||||
@ -116,6 +136,51 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
||||
))
|
||||
);
|
||||
|
||||
// Check self-termination
|
||||
engine
|
||||
.register_custom_syntax(&["test1", "$block$"], true, |_, _| Ok(Dynamic::UNIT))?
|
||||
.register_custom_syntax(&["test2", "}"], true, |_, _| Ok(Dynamic::UNIT))?
|
||||
.register_custom_syntax(&["test3", ";"], true, |_, _| Ok(Dynamic::UNIT))?;
|
||||
|
||||
assert_eq!(engine.eval::<INT>("test1 { x = y + z; } 42")?, 42);
|
||||
assert_eq!(engine.eval::<INT>("test2 } 42")?, 42);
|
||||
assert_eq!(engine.eval::<INT>("test3; 42")?, 42);
|
||||
|
||||
// Register the custom syntax: var x = ???
|
||||
engine.register_custom_syntax(
|
||||
&["var", "$ident$", "=", "$expr$"],
|
||||
true,
|
||||
|context, inputs| {
|
||||
let var_name = inputs[0].get_variable_name().unwrap().to_string();
|
||||
let expr = &inputs[1];
|
||||
|
||||
// Evaluate the expression
|
||||
let value = context.eval_expression_tree(expr)?;
|
||||
|
||||
if !context.scope().is_constant(&var_name).unwrap_or(false) {
|
||||
context.scope_mut().set_value(var_name, value);
|
||||
Ok(Dynamic::UNIT)
|
||||
} else {
|
||||
Err(format!("variable {} is constant", var_name).into())
|
||||
}
|
||||
},
|
||||
)?;
|
||||
|
||||
let mut scope = Scope::new();
|
||||
|
||||
assert_eq!(
|
||||
engine.eval_with_scope::<INT>(&mut scope, "var foo = 42; foo")?,
|
||||
42
|
||||
);
|
||||
assert_eq!(scope.get_value::<INT>("foo"), Some(42));
|
||||
assert_eq!(scope.len(), 1);
|
||||
assert_eq!(
|
||||
engine.eval_with_scope::<INT>(&mut scope, "var foo = 123; foo")?,
|
||||
123
|
||||
);
|
||||
assert_eq!(scope.get_value::<INT>("foo"), Some(123));
|
||||
assert_eq!(scope.len(), 1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -208,7 +208,7 @@ fn test_max_map_size() -> Result<(), Box<EvalAltResult>> {
|
||||
|
||||
assert!(matches!(
|
||||
*engine
|
||||
.consume(
|
||||
.run(
|
||||
"
|
||||
let x = #{};
|
||||
loop { x.a = x; }
|
||||
|
@ -368,7 +368,7 @@ fn test_module_from_ast() -> Result<(), Box<EvalAltResult>> {
|
||||
);
|
||||
assert!(matches!(
|
||||
*engine
|
||||
.consume(r#"import "testing" as ttt; ttt::hidden()"#)
|
||||
.run(r#"import "testing" as ttt; ttt::hidden()"#)
|
||||
.expect_err("should error"),
|
||||
EvalAltResult::ErrorFunctionNotFound(fn_name, _) if fn_name == "ttt::hidden ()"
|
||||
));
|
||||
@ -498,7 +498,7 @@ fn test_module_ast_namespace2() -> Result<(), Box<EvalAltResult>> {
|
||||
static_modules.insert("test_module", module);
|
||||
engine.set_module_resolver(static_modules);
|
||||
|
||||
engine.consume(SCRIPT)?;
|
||||
engine.run(SCRIPT)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -87,11 +87,11 @@ fn test_plugins_package() -> Result<(), Box<EvalAltResult>> {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
{
|
||||
assert_eq!(engine.eval::<INT>("let a = [1, 2, 3]; a.foo")?, 1);
|
||||
engine.consume("const A = [1, 2, 3]; A.no_effect(42);")?;
|
||||
engine.consume("const A = [1, 2, 3]; A.no_effect = 42;")?;
|
||||
engine.run("const A = [1, 2, 3]; A.no_effect(42);")?;
|
||||
engine.run("const A = [1, 2, 3]; A.no_effect = 42;")?;
|
||||
|
||||
assert!(
|
||||
matches!(*engine.consume("const A = [1, 2, 3]; A.test(42);").expect_err("should error"),
|
||||
matches!(*engine.run("const A = [1, 2, 3]; A.test(42);").expect_err("should error"),
|
||||
EvalAltResult::ErrorAssignmentToConstant(x, _) if x == "array")
|
||||
)
|
||||
}
|
||||
|
@ -50,10 +50,10 @@ fn test_print_debug() -> Result<(), Box<EvalAltResult>> {
|
||||
});
|
||||
|
||||
// Evaluate script
|
||||
engine.consume("print(40 + 2)")?;
|
||||
engine.run("print(40 + 2)")?;
|
||||
let mut ast = engine.compile(r#"let x = "hello!"; debug(x)"#)?;
|
||||
ast.set_source("world");
|
||||
engine.consume_ast(&ast)?;
|
||||
engine.run_ast(&ast)?;
|
||||
|
||||
// 'logbook' captures all the 'print' and 'debug' output
|
||||
assert_eq!(logbook.read().unwrap().len(), 2);
|
||||
@ -96,7 +96,7 @@ fn test_print_custom_type() -> Result<(), Box<EvalAltResult>> {
|
||||
.register_fn("debug", |x: &mut MyStruct| x.to_string())
|
||||
.register_fn("new_ts", || MyStruct { field: 42 });
|
||||
|
||||
engine.consume("let x = new_ts(); debug(x);")?;
|
||||
engine.run("let x = new_ts(); debug(x);")?;
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
assert_eq!(
|
||||
|
@ -102,7 +102,7 @@ fn test_serde_ser_struct() -> Result<(), Box<EvalAltResult>> {
|
||||
assert!(obj["b"].as_bool().unwrap());
|
||||
assert_eq!(Ok(42), map["int"].as_int());
|
||||
assert_eq!(seq.len(), 3);
|
||||
assert_eq!("kitty", seq.remove(1).as_string().unwrap());
|
||||
assert_eq!("kitty", seq.remove(1).into_string().unwrap());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -116,10 +116,10 @@ fn test_serde_ser_unit_enum() -> Result<(), Box<EvalAltResult>> {
|
||||
}
|
||||
|
||||
let d = to_dynamic(MyEnum::VariantFoo)?;
|
||||
assert_eq!("VariantFoo", d.as_string().unwrap());
|
||||
assert_eq!("VariantFoo", d.into_string().unwrap());
|
||||
|
||||
let d = to_dynamic(MyEnum::VariantBar)?;
|
||||
assert_eq!("VariantBar", d.as_string().unwrap());
|
||||
assert_eq!("VariantBar", d.into_string().unwrap());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -145,7 +145,7 @@ fn test_serde_ser_externally_tagged_enum() -> Result<(), Box<EvalAltResult>> {
|
||||
assert_eq!(
|
||||
"VariantUnit",
|
||||
to_dynamic(MyEnum::VariantUnit)?
|
||||
.as_immutable_string()
|
||||
.into_immutable_string()
|
||||
.unwrap()
|
||||
.as_str()
|
||||
);
|
||||
@ -203,7 +203,7 @@ fn test_serde_ser_internally_tagged_enum() -> Result<(), Box<EvalAltResult>> {
|
||||
"VariantEmptyStruct",
|
||||
map.remove("tag")
|
||||
.unwrap()
|
||||
.as_immutable_string()
|
||||
.into_immutable_string()
|
||||
.unwrap()
|
||||
.as_str()
|
||||
);
|
||||
@ -214,7 +214,7 @@ fn test_serde_ser_internally_tagged_enum() -> Result<(), Box<EvalAltResult>> {
|
||||
"VariantStruct",
|
||||
map.remove("tag")
|
||||
.unwrap()
|
||||
.as_immutable_string()
|
||||
.into_immutable_string()
|
||||
.unwrap()
|
||||
.as_str()
|
||||
);
|
||||
@ -247,7 +247,7 @@ fn test_serde_ser_adjacently_tagged_enum() -> Result<(), Box<EvalAltResult>> {
|
||||
"VariantUnit",
|
||||
map.remove("tag")
|
||||
.unwrap()
|
||||
.as_immutable_string()
|
||||
.into_immutable_string()
|
||||
.unwrap()
|
||||
.as_str()
|
||||
);
|
||||
@ -260,7 +260,7 @@ fn test_serde_ser_adjacently_tagged_enum() -> Result<(), Box<EvalAltResult>> {
|
||||
"VariantUnitTuple",
|
||||
map.remove("tag")
|
||||
.unwrap()
|
||||
.as_immutable_string()
|
||||
.into_immutable_string()
|
||||
.unwrap()
|
||||
.as_str()
|
||||
);
|
||||
@ -274,7 +274,7 @@ fn test_serde_ser_adjacently_tagged_enum() -> Result<(), Box<EvalAltResult>> {
|
||||
"VariantNewtype",
|
||||
map.remove("tag")
|
||||
.unwrap()
|
||||
.as_immutable_string()
|
||||
.into_immutable_string()
|
||||
.unwrap()
|
||||
.as_str()
|
||||
);
|
||||
@ -289,7 +289,7 @@ fn test_serde_ser_adjacently_tagged_enum() -> Result<(), Box<EvalAltResult>> {
|
||||
"VariantTuple",
|
||||
map.remove("tag")
|
||||
.unwrap()
|
||||
.as_immutable_string()
|
||||
.into_immutable_string()
|
||||
.unwrap()
|
||||
.as_str()
|
||||
);
|
||||
@ -305,7 +305,7 @@ fn test_serde_ser_adjacently_tagged_enum() -> Result<(), Box<EvalAltResult>> {
|
||||
"VariantEmptyStruct",
|
||||
map.remove("tag")
|
||||
.unwrap()
|
||||
.as_immutable_string()
|
||||
.into_immutable_string()
|
||||
.unwrap()
|
||||
.as_str()
|
||||
);
|
||||
@ -316,7 +316,7 @@ fn test_serde_ser_adjacently_tagged_enum() -> Result<(), Box<EvalAltResult>> {
|
||||
let mut map = to_dynamic(MyEnum::VariantStruct { a: 123 })?.cast::<Map>();
|
||||
assert_eq!(
|
||||
"VariantStruct",
|
||||
map.remove("tag").unwrap().as_string().unwrap()
|
||||
map.remove("tag").unwrap().into_string().unwrap()
|
||||
);
|
||||
let mut map_inner = map.remove("content").unwrap().cast::<Map>();
|
||||
assert!(map.is_empty());
|
||||
|
@ -74,7 +74,7 @@ fn test_side_effects_print() -> Result<(), Box<EvalAltResult>> {
|
||||
let logger = result.clone();
|
||||
engine.on_print(move |s| logger.write().unwrap().push_str(s));
|
||||
|
||||
engine.consume("print(40 + 2);")?;
|
||||
engine.run("print(40 + 2);")?;
|
||||
|
||||
assert_eq!(*result.read().unwrap(), "42");
|
||||
Ok(())
|
||||
|
@ -57,9 +57,9 @@ fn test_infinite_loops() -> Result<(), Box<EvalAltResult>> {
|
||||
|
||||
engine.set_max_operations(1024);
|
||||
|
||||
assert!(engine.consume("loop {}").is_err());
|
||||
assert!(engine.consume("while true {}").is_err());
|
||||
assert!(engine.consume("do {} while true").is_err());
|
||||
assert!(engine.run("loop {}").is_err());
|
||||
assert!(engine.run("while true {}").is_err());
|
||||
assert!(engine.run("do {} while true").is_err());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user