From e0cae4546c1b6df706ccd7b4d051dcdbbef87048 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 11 Aug 2021 16:09:04 +0800 Subject: [PATCH 01/19] Polyfiill some functions for decimal+no_float. --- CHANGELOG.md | 11 ++++++++++- src/packages/math_basic.rs | 16 ++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31a8d41a..6d52f811 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,15 @@ Rhai Release Notes ================== +Version 1.1.0 +============= + +Enhancements +------------ + +* `parse_float()`, `PI()` and `E()` now defer to `Decimal` under `no_float` if `decimal` is turned on. + + Version 1.0.2 ============= @@ -105,7 +114,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>>` is added for `Dynamic` mapping directly to a shared value, together with support for `Dynamic::from`. +* `From< Shared< Locked > >` 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 diff --git a/src/packages/math_basic.rs b/src/packages/math_basic.rs index 2b07a5bc..53c0431b 100644 --- a/src/packages/math_basic.rs +++ b/src/packages/math_basic.rs @@ -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> { + super::parse_decimal(s) + } + } + #[rhai_fn(return_raw)] pub fn sqrt(x: Decimal) -> Result> { x.sqrt() From b21deaf052c5c5855fd06352515c04202f8bf4b2 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 10 Jul 2021 15:50:31 +0800 Subject: [PATCH 02/19] Support $symbol$ in custom syntax. --- CHANGELOG.md | 1 + src/ast.rs | 4 +-- src/custom_syntax.rs | 40 ++++++++++++++++++------------ src/engine_api.rs | 4 +-- src/engine_settings.rs | 4 +-- src/error_parsing.rs | 2 ++ src/parse.rs | 55 +++++++++++++++++++++++++++--------------- src/token.rs | 14 +++++------ tests/custom_syntax.rs | 40 ++++++++++++++++++++++-------- 9 files changed, 105 insertions(+), 59 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d52f811..1a0673f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ Version 1.1.0 Enhancements ------------ +* `$symbol$` is supported in custom syntax to match any symbol. * `parse_float()`, `PI()` and `E()` now defer to `Decimal` under `no_float` if `decimal` is turned on. diff --git a/src/ast.rs b/src/ast.rs index cb366bb7..4eb51428 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1552,8 +1552,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), diff --git a/src/custom_syntax.rs b/src/custom_syntax.rs index 6e98eee1..5325d98b 100644 --- a/src/custom_syntax.rs +++ b/src/custom_syntax.rs @@ -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 = 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 @@ -226,7 +234,7 @@ impl Engine { s if segments.is_empty() && token .as_ref() - .map_or(false, |v| v.is_keyword() || v.is_reserved()) => + .map_or(false, |v| v.is_standard_keyword() || v.is_reserved()) => { return Err(LexError::ImproperSymbol( s.to_string(), diff --git a/src/engine_api.rs b/src/engine_api.rs index 9b29a6ba..48e0a8bd 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -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( diff --git a/src/engine_settings.rs b/src/engine_settings.rs index 06dd4f7b..e7e000b5 100644 --- a/src/engine_settings.rs +++ b/src/engine_settings.rs @@ -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())); } diff --git a/src/error_parsing.rs b/src/error_parsing.rs index 396c7957..1094028c 100644 --- a/src/error_parsing.rs +++ b/src/error_parsing.rs @@ -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() { diff --git a/src/parse.rs b/src/parse.rs index a98154cc..b5bbe171 100644 --- a/src/parse.rs +++ b/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, @@ -2016,6 +2027,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 +2052,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 +2065,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 +2079,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 +2091,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)) diff --git a/src/token.rs b/src/token.rs index 3fd2d8b3..61c447aa 100644 --- a/src/token.rs +++ b/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 { diff --git a/tests/custom_syntax.rs b/tests/custom_syntax.rs index a5947453..f10422ed 100644 --- a/tests/custom_syntax.rs +++ b/tests/custom_syntax.rs @@ -1,4 +1,6 @@ -use rhai::{Dynamic, Engine, EvalAltResult, LexError, ParseErrorType, Position, INT}; +use rhai::{ + Dynamic, Engine, EvalAltResult, ImmutableString, LexError, ParseErrorType, Position, INT, +}; #[test] fn test_custom_syntax() -> Result<(), Box> { @@ -19,21 +21,32 @@ fn test_custom_syntax() -> Result<(), Box> { 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::().unwrap(); - let stmt = &inputs[2]; - let condition = &inputs[3]; + let op = inputs[1].get_literal_value::().unwrap(); + let max = inputs[2].get_literal_value::().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> { }, )?; + assert!(matches!( + *engine + .consume("let foo = (exec [x<<15] -> { x += 2 } while x < 42) * 10;") + .expect_err("should error"), + EvalAltResult::ErrorRuntime(_, _) + )); + assert_eq!( engine.eval::( " 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> { engine.eval::( " 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> { assert_eq!( engine.eval::( " - 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> { engine.eval::( " let foo = 123; - exec [x;15] -> { x += 1 } while x < 42; + exec [x<15] -> { x += 1 } while x < 42; foo + x + x1 + x2 + x3 " )?, From 94674679d9a9dcaa1ccf9d0b55d162a6863ea73e Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 24 Jul 2021 16:24:59 +0800 Subject: [PATCH 03/19] Rename as_string and as_immutable_string. --- CHANGELOG.md | 1 + codegen/Cargo.toml | 2 +- codegen/src/function.rs | 4 ++-- codegen/src/test/function.rs | 2 +- codegen/src/test/module.rs | 4 ++-- src/dynamic.rs | 40 +++++++++++++++++++++++++++++++++--- src/fn_call.rs | 12 +++++------ src/fn_register.rs | 2 +- src/packages/string_basic.rs | 2 +- src/serde/ser.rs | 4 ++-- tests/closures.rs | 2 +- tests/serde.rs | 24 +++++++++++----------- 12 files changed, 67 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a0673f5..d2819836 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Enhancements ------------ * `$symbol$` is supported in custom syntax to match any symbol. +* `Dynamic::as_string` and `Dynamic::as_immutable_string` are deprecated and replaced by `into_string` and `into_immutable_string` respectively. * `parse_float()`, `PI()` and `E()` now defer to `Decimal` under `no_float` if `decimal` is turned on. diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml index b950aff0..854ad08a 100644 --- a/codegen/Cargo.toml +++ b/codegen/Cargo.toml @@ -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" diff --git a/codegen/src/function.rs b/codegen/src/function.rs index 8694f266..3158cb33 100644 --- a/codegen/src/function.rs +++ b/codegen/src/function.rs @@ -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() ) } _ => { diff --git a/codegen/src/test/function.rs b/codegen/src/test/function.rs index 6d9eaa33..bca0dfdf 100644 --- a/codegen/src/test/function.rs +++ b/codegen/src/test/function.rs @@ -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))) } diff --git a/codegen/src/test/module.rs b/codegen/src/test/module.rs index 620b3a1c..abcdc0cc 100644 --- a/codegen/src/test/module.rs +++ b/codegen/src/test/module.rs @@ -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))) } diff --git a/src/dynamic.rs b/src/dynamic.rs index 401e3907..8d401697 100644 --- a/src/dynamic.rs +++ b/src/dynamic.rs @@ -1816,14 +1816,48 @@ 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] + /// + /// # 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.0.1", + note = "this method is deprecated; use `into_string` instead" + )] + #[inline(always)] pub fn as_string(self) -> Result { - self.as_immutable_string().map(ImmutableString::into_owned) + self.into_string() + } + /// 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(always)] + pub fn into_string(self) -> Result { + 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. + /// + /// # 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.0.1", + note = "this method is deprecated; use `into_string` instead" + )] + #[inline(always)] + pub fn as_immutable_string(self) -> Result { + self.into_immutable_string() } /// 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 { + pub fn into_immutable_string(self) -> Result { match self.0 { Union::Str(s, _, _) => Ok(s), #[cfg(not(feature = "no_closure"))] diff --git a/src/fn_call.rs b/src/fn_call.rs index 8ed8430c..4ad24723 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -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::()).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::()).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::(typ, arg_pos)) .and_then(FnPtr::try_from) .map(Into::::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::(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::(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::(typ, pos))?; let result = self.eval_script_expr_in_place(scope, mods, state, lib, script, pos, level + 1); diff --git a/src/fn_register.rs b/src/fn_register.rs index 57a4e34f..592c3cac 100644 --- a/src/fn_register.rs +++ b/src/fn_register.rs @@ -53,7 +53,7 @@ pub fn by_value(data: &mut Dynamic) -> T { } else if TypeId::of::() == TypeId::of::() { // 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 { diff --git a/src/packages/string_basic.rs b/src/packages/string_basic.rs index 1bb901c8..73193a28 100644 --- a/src/packages/string_basic.rs +++ b/src/packages/string_basic.rs @@ -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::() => 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(), diff --git a/src/serde/ser.rs b/src/serde/ser.rs index c4b7d56f..f42253dc 100644 --- a/src/serde/ser.rs +++ b/src/serde/ser.rs @@ -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)?; diff --git a/tests/closures.rs b/tests/closures.rs index 922a3cd3..c68038f5 100644 --- a/tests/closures.rs +++ b/tests/closures.rs @@ -343,7 +343,7 @@ fn test_closures_external() -> Result<(), Box> { // 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(()) } diff --git a/tests/serde.rs b/tests/serde.rs index a02645f3..ac98dc37 100644 --- a/tests/serde.rs +++ b/tests/serde.rs @@ -102,7 +102,7 @@ fn test_serde_ser_struct() -> Result<(), Box> { 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> { } 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> { 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> { "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> { "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> { "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> { "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> { "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> { "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> { "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> { let mut map = to_dynamic(MyEnum::VariantStruct { a: 123 })?.cast::(); 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::(); assert!(map.is_empty()); From ffb1531e0e9810a746c3a3588394519ef1fdf221 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 25 Jul 2021 11:50:31 +0800 Subject: [PATCH 04/19] New constants for Dynamic. --- CHANGELOG.md | 1 + src/dynamic.rs | 62 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2819836..98e4ad65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Enhancements * `$symbol$` is supported in custom syntax to match any symbol. * `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`. * `parse_float()`, `PI()` and `E()` now defer to `Decimal` under `no_float` if `decimal` is turned on. diff --git a/src/dynamic.rs b/src/dynamic.rs index 8d401697..b1126623 100644 --- a/src/dynamic.rs +++ b/src/dynamic.rs @@ -830,8 +830,14 @@ impl Dynamic { pub const ONE: Dynamic = Self(Union::Int(1, DEFAULT_TAG_VALUE, ReadWrite)); /// A [`Dynamic`] containing the integer two. pub const TWO: Dynamic = Self(Union::Int(2, DEFAULT_TAG_VALUE, ReadWrite)); + /// A [`Dynamic`] containing the integer three. + pub const THREE: Dynamic = Self(Union::Int(3, DEFAULT_TAG_VALUE, ReadWrite)); /// A [`Dynamic`] containing the integer ten. pub const TEN: Dynamic = Self(Union::Int(10, DEFAULT_TAG_VALUE, ReadWrite)); + /// A [`Dynamic`] containing the integer one hundred. + pub const HUNDRED: Dynamic = Self(Union::Int(100, DEFAULT_TAG_VALUE, ReadWrite)); + /// A [`Dynamic`] containing the integer one thousand. + pub const THOUSAND: Dynamic = Self(Union::Int(1000, DEFAULT_TAG_VALUE, ReadWrite)); /// A [`Dynamic`] containing the integer negative one. pub const NEGATIVE_ONE: Dynamic = Self(Union::Int(-1, DEFAULT_TAG_VALUE, ReadWrite)); /// A [`Dynamic`] containing `0.0`. @@ -870,7 +876,25 @@ impl Dynamic { DEFAULT_TAG_VALUE, ReadWrite, )); - /// A [`Dynamic`] containing the `-1.0`. + /// A [`Dynamic`] containing `100.0`. + /// + /// Not available under `no_float`. + #[cfg(not(feature = "no_float"))] + pub const FLOAT_HUNDRED: Dynamic = Self(Union::Float( + FloatWrapper::new_const(100.0), + DEFAULT_TAG_VALUE, + ReadWrite, + )); + /// A [`Dynamic`] containing `1000.0`. + /// + /// Not available under `no_float`. + #[cfg(not(feature = "no_float"))] + pub const FLOAT_THOUSAND: Dynamic = Self(Union::Float( + FloatWrapper::new_const(1000.0), + DEFAULT_TAG_VALUE, + ReadWrite, + )); + /// A [`Dynamic`] containing `-1.0`. /// /// Not available under `no_float`. #[cfg(not(feature = "no_float"))] @@ -879,6 +903,42 @@ impl Dynamic { DEFAULT_TAG_VALUE, ReadWrite, )); + /// A [`Dynamic`] containing π. + /// + /// Not available under `no_float`. + #[cfg(not(feature = "no_float"))] + pub const FLOAT_PI: Dynamic = Self(Union::Float( + #[cfg(not(feature = "f32_float"))] + FloatWrapper::new_const(std::f64::consts::PI), + #[cfg(feature = "f32_float")] + FloatWrapper::new_const(std::f32::consts::PI), + DEFAULT_TAG_VALUE, + ReadWrite, + )); + /// A [`Dynamic`] containing π/2. + /// + /// Not available under `no_float`. + #[cfg(not(feature = "no_float"))] + pub const FLOAT_HALF_PI: Dynamic = Self(Union::Float( + #[cfg(not(feature = "f32_float"))] + FloatWrapper::new_const(std::f64::consts::PI / 2.0), + #[cfg(feature = "f32_float")] + FloatWrapper::new_const(std::f32::consts::PI / 2.0), + DEFAULT_TAG_VALUE, + ReadWrite, + )); + /// A [`Dynamic`] containing 2π. + /// + /// Not available under `no_float`. + #[cfg(not(feature = "no_float"))] + pub const FLOAT_TWO_PI: Dynamic = Self(Union::Float( + #[cfg(not(feature = "f32_float"))] + FloatWrapper::new_const(2.0 * std::f64::consts::PI), + #[cfg(feature = "f32_float")] + FloatWrapper::new_const(2.0 * std::f32::consts::PI), + DEFAULT_TAG_VALUE, + ReadWrite, + )); /// Get the [`AccessMode`] for this [`Dynamic`]. #[must_use] From 3127f9a8af6cfe4e73d26421183dbb1dbc950a8b Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 25 Jul 2021 22:48:40 +0800 Subject: [PATCH 05/19] Add new constant from functions for Dynamic. --- CHANGELOG.md | 1 + src/dynamic.rs | 169 ++++++++++++++++++++++++++++--------------------- 2 files changed, 98 insertions(+), 72 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98e4ad65..f02a2c9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Enhancements * `$symbol$` is supported in custom syntax to match any symbol. * `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. diff --git a/src/dynamic.rs b/src/dynamic.rs index b1126623..0a6b50d2 100644 --- a/src/dynamic.rs +++ b/src/dynamic.rs @@ -10,6 +10,7 @@ use std::{ fmt, hash::{Hash, Hasher}, ops::{Deref, DerefMut}, + str::FromStr, }; #[cfg(not(feature = "no_float"))] @@ -819,126 +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: Dynamic = Self(Union::Int(3, DEFAULT_TAG_VALUE, ReadWrite)); + 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: Dynamic = Self(Union::Int(100, DEFAULT_TAG_VALUE, ReadWrite)); + pub const HUNDRED: Self = Self::from_int(100); /// A [`Dynamic`] containing the integer one thousand. - pub const THOUSAND: Dynamic = Self(Union::Int(1000, DEFAULT_TAG_VALUE, ReadWrite)); + 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, - )); + 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_HUNDRED: Dynamic = Self(Union::Float( - FloatWrapper::new_const(100.0), - DEFAULT_TAG_VALUE, - ReadWrite, - )); + 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: Dynamic = Self(Union::Float( - FloatWrapper::new_const(1000.0), - DEFAULT_TAG_VALUE, - ReadWrite, - )); + 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: Dynamic = Self(Union::Float( - FloatWrapper::new_const(-1.0), - DEFAULT_TAG_VALUE, - ReadWrite, - )); + pub const FLOAT_NEGATIVE_ONE: Self = Self::from_float(-1.0); /// A [`Dynamic`] containing π. /// /// Not available under `no_float`. #[cfg(not(feature = "no_float"))] - pub const FLOAT_PI: Dynamic = Self(Union::Float( + #[cfg(not(feature = "f32_float"))] + pub const FLOAT_PI: Self = Self::from_float( #[cfg(not(feature = "f32_float"))] - FloatWrapper::new_const(std::f64::consts::PI), + { + std::f64::consts::PI + }, #[cfg(feature = "f32_float")] - FloatWrapper::new_const(std::f32::consts::PI), - DEFAULT_TAG_VALUE, - ReadWrite, - )); + { + std::f32::consts::PI + }, + ); /// A [`Dynamic`] containing π/2. /// /// Not available under `no_float`. #[cfg(not(feature = "no_float"))] - pub const FLOAT_HALF_PI: Dynamic = Self(Union::Float( + pub const FLOAT_HALF_PI: Self = Self::from_float( #[cfg(not(feature = "f32_float"))] - FloatWrapper::new_const(std::f64::consts::PI / 2.0), + { + std::f64::consts::PI / 2.0 + }, #[cfg(feature = "f32_float")] - FloatWrapper::new_const(std::f32::consts::PI / 2.0), - DEFAULT_TAG_VALUE, - ReadWrite, - )); + { + 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: Dynamic = Self(Union::Float( + pub const FLOAT_TWO_PI: Self = Self::from_float( #[cfg(not(feature = "f32_float"))] - FloatWrapper::new_const(2.0 * std::f64::consts::PI), + { + std::f64::consts::PI * 2.0 + }, #[cfg(feature = "f32_float")] - FloatWrapper::new_const(2.0 * std::f32::consts::PI), - DEFAULT_TAG_VALUE, - ReadWrite, - )); + { + 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] @@ -1882,10 +1906,7 @@ impl Dynamic { /// This method is deprecated. Use [`into_string`][Dynamic::into_string] instead. /// /// This method will be removed in the next major version. - #[deprecated( - since = "1.0.1", - note = "this method is deprecated; use `into_string` instead" - )] + #[deprecated(since = "1.0.1", note = "use `into_string` instead")] #[inline(always)] pub fn as_string(self) -> Result { self.into_string() @@ -1906,10 +1927,7 @@ impl Dynamic { /// 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.0.1", - note = "this method is deprecated; use `into_string` instead" - )] + #[deprecated(since = "1.0.1", note = "use `into_immutable_string` instead")] #[inline(always)] pub fn as_immutable_string(self) -> Result { self.into_immutable_string() @@ -1994,6 +2012,13 @@ impl From<&ImmutableString> for Dynamic { value.clone().into() } } +impl FromStr for Dynamic { + type Err = (); + + fn from_str(value: &str) -> Result { + Ok(Self(Union::Str(value.into(), DEFAULT_TAG_VALUE, ReadWrite))) + } +} #[cfg(not(feature = "no_index"))] impl Dynamic { /// Create a [`Dynamic`] from an [`Array`]. From e0125a1033ac981cd3bbe0693d781f35425cb16e Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 2 Aug 2021 10:24:03 +0800 Subject: [PATCH 06/19] Allow self-terminating custom syntax. --- CHANGELOG.md | 1 + src/ast.rs | 17 ++++++++++++++++- src/parse.rs | 19 ++++++++++++++++--- tests/custom_syntax.rs | 10 ++++++++++ 4 files changed, 43 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f02a2c9a..9f75f681 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Enhancements ------------ * `$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`. diff --git a/src/ast.rs b/src/ast.rs index 4eb51428..968f7ac7 100644 --- a/src/ast.rs +++ b/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, + /// 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. diff --git a/src/parse.rs b/src/parse.rs index b5bbe171..b0656b44 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -2004,16 +2004,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)), @@ -2120,11 +2121,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, diff --git a/tests/custom_syntax.rs b/tests/custom_syntax.rs index f10422ed..0d130be4 100644 --- a/tests/custom_syntax.rs +++ b/tests/custom_syntax.rs @@ -136,6 +136,16 @@ fn test_custom_syntax() -> Result<(), Box> { )) ); + // 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::("test1 { x = y + z; } 42")?, 42); + assert_eq!(engine.eval::("test2 } 42")?, 42); + assert_eq!(engine.eval::("test3; 42")?, 42); + Ok(()) } From 180ad772246b12a577077cb642c94cfdd03730db Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 6 Aug 2021 14:46:27 +0800 Subject: [PATCH 07/19] Change Engine::consume_XXX to Engine::run_XXX. --- CHANGELOG.md | 1 + benches/eval_array.rs | 10 ++-- benches/eval_expression.rs | 20 +++---- benches/eval_map.rs | 8 +-- benches/eval_module.rs | 4 +- benches/eval_scope.rs | 8 +-- benches/eval_type.rs | 8 +-- benches/iterations.rs | 2 +- benches/primes.rs | 2 +- examples/hello.rs | 2 +- examples/strings.rs | 2 +- examples/threading.rs | 2 +- src/bin/rhai-run.rs | 2 +- src/dynamic.rs | 4 +- src/engine_api.rs | 114 +++++++++++++++++++++++++++++++++---- tests/constants.rs | 8 +-- tests/custom_syntax.rs | 4 +- tests/data_size.rs | 2 +- tests/modules.rs | 4 +- tests/plugins.rs | 6 +- tests/print.rs | 6 +- tests/side_effects.rs | 2 +- tests/while_loop.rs | 6 +- 23 files changed, 160 insertions(+), 67 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f75f681..013025aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ Version 1.1.0 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. diff --git a/benches/eval_array.rs b/benches/eval_array.rs index 0689bf4f..9dcf2129 100644 --- a/benches/eval_array.rs +++ b/benches/eval_array.rs @@ -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()); } diff --git a/benches/eval_expression.rs b/benches/eval_expression.rs index 9ef69f00..07050ebf 100644 --- a/benches/eval_expression.rs +++ b/benches/eval_expression.rs @@ -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()); } diff --git a/benches/eval_map.rs b/benches/eval_map.rs index afe8d676..df49dcdf 100644 --- a/benches/eval_map.rs +++ b/benches/eval_map.rs @@ -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()); } diff --git a/benches/eval_module.rs b/benches/eval_module.rs index 9b053bcd..3bb4e04e 100644 --- a/benches/eval_module.rs +++ b/benches/eval_module.rs @@ -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()); } diff --git a/benches/eval_scope.rs b/benches/eval_scope.rs index 5d38f7d7..2237fa8a 100644 --- a/benches/eval_scope.rs +++ b/benches/eval_scope.rs @@ -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()); } diff --git a/benches/eval_type.rs b/benches/eval_type.rs index 469e6332..7cee4394 100644 --- a/benches/eval_type.rs +++ b/benches/eval_type.rs @@ -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()); } diff --git a/benches/iterations.rs b/benches/iterations.rs index b70e9432..243756ae 100644 --- a/benches/iterations.rs +++ b/benches/iterations.rs @@ -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] diff --git a/benches/primes.rs b/benches/primes.rs index 1b1b022b..e6a059fc 100644 --- a/benches/primes.rs +++ b/benches/primes.rs @@ -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()); } diff --git a/examples/hello.rs b/examples/hello.rs index 4133cd3f..7de8898e 100644 --- a/examples/hello.rs +++ b/examples/hello.rs @@ -3,7 +3,7 @@ use rhai::{Engine, EvalAltResult, INT}; fn main() -> Result<(), Box> { let engine = Engine::new(); - engine.consume(r#"print("hello, world!")"#)?; + engine.run(r#"print("hello, world!")"#)?; let result = engine.eval::("40 + 2")?; diff --git a/examples/strings.rs b/examples/strings.rs index ba5749ec..6d0489ef 100644 --- a/examples/strings.rs +++ b/examples/strings.rs @@ -61,7 +61,7 @@ fn main() -> Result<(), Box> { println!("Line: {}", input.replace('\r', "\\r").replace('\n', "\\n")); - engine.consume_with_scope( + engine.run_with_scope( &mut scope, r#" display("Length", x.len()); diff --git a/examples/threading.rs b/examples/threading.rs index ad80aedb..f96a8866 100644 --- a/examples/threading.rs +++ b/examples/threading.rs @@ -34,7 +34,7 @@ fn main() { // Run script engine - .consume( + .run( r#" print("Starting script loop..."); diff --git a/src/bin/rhai-run.rs b/src/bin/rhai-run.rs index 4d1749df..567d4b45 100644 --- a/src/bin/rhai-run.rs +++ b/src/bin/rhai-run.rs @@ -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(); diff --git a/src/dynamic.rs b/src/dynamic.rs index 0a6b50d2..15862f6a 100644 --- a/src/dynamic.rs +++ b/src/dynamic.rs @@ -1906,7 +1906,7 @@ impl Dynamic { /// This method is deprecated. Use [`into_string`][Dynamic::into_string] instead. /// /// This method will be removed in the next major version. - #[deprecated(since = "1.0.1", note = "use `into_string` instead")] + #[deprecated(since = "1.1.0", note = "use `into_string` instead")] #[inline(always)] pub fn as_string(self) -> Result { self.into_string() @@ -1927,7 +1927,7 @@ impl Dynamic { /// 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.0.1", note = "use `into_immutable_string` instead")] + #[deprecated(since = "1.1.0", note = "use `into_immutable_string` instead")] #[inline(always)] pub fn as_immutable_string(self) -> Result { self.into_immutable_string() diff --git a/src/engine_api.rs b/src/engine_api.rs index 48e0a8bd..00e531a8 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -1745,16 +1745,39 @@ impl Engine { /// 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> { - Self::read_file(path).and_then(|contents| self.consume(&contents)) + self.run_file(path) + } + /// 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 run_file(&self, path: std::path::PathBuf) -> Result<(), Box> { + 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. /// /// 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)] @@ -1763,21 +1786,62 @@ impl Engine { scope: &mut Scope, path: std::path::PathBuf, ) -> Result<(), Box> { - Self::read_file(path).and_then(|contents| self.consume_with_scope(scope, &contents)) + self.run_file_with_scope(scope, path) + } + /// 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 run_file_with_scope( + &self, + scope: &mut Scope, + path: std::path::PathBuf, + ) -> Result<(), Box> { + 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. + /// + /// # 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> { - self.consume_with_scope(&mut Default::default(), script) + self.run(script) + } + /// Evaluate a script, returning any error (if any). + #[inline(always)] + pub fn run(&self, script: &str) -> Result<(), Box> { + 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. - #[inline] + /// + /// # 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> { + self.run_with_scope(scope, script) + } + /// Evaluate a script with own scope, returning any error (if any). + #[inline] + pub fn run_with_scope( + &self, + scope: &mut Scope, + script: &str, ) -> Result<(), Box> { let scripts = [script]; let (stream, tokenizer_control) = self.lex_raw(&scripts, None); @@ -1790,21 +1854,49 @@ 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. + /// + /// # 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> { - self.consume_ast_with_scope(&mut Default::default(), ast) + self.run_ast(ast) + } + /// Evaluate an AST, returning any error (if any). + #[inline(always)] + pub fn run_ast(&self, ast: &AST) -> Result<(), Box> { + 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. - #[inline] + /// + /// # 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> { + self.run_ast_with_scope(scope, ast) + } + /// Evaluate an [`AST`] with own scope, returning any error (if any). + #[inline] + pub fn run_ast_with_scope( + &self, + scope: &mut Scope, + ast: &AST, ) -> Result<(), Box> { let mods = &mut Default::default(); let mut state = EvalState::new(); @@ -2151,8 +2243,8 @@ impl Engine { /// } /// }); /// - /// engine.consume("for x in range(0, 50000) {}") - /// .expect_err("should error"); + /// engine.run("for x in range(0, 50000) {}") + /// .expect_err("should error"); /// /// assert_eq!(*result.read().unwrap(), 9600); /// @@ -2186,7 +2278,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 +2311,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""#); diff --git a/tests/constants.rs b/tests/constants.rs index c64ecb1b..9f3b35e0 100644 --- a/tests/constants.rs +++ b/tests/constants.rs @@ -15,7 +15,7 @@ fn test_constant() -> Result<(), Box> { #[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> { 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> { assert!(matches!( *engine - .consume( + .run( " const MY_NUMBER = new_ts(); MY_NUMBER.value = 42; @@ -118,7 +118,7 @@ fn test_constant_mut() -> Result<(), Box> { 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(_, _) )); diff --git a/tests/custom_syntax.rs b/tests/custom_syntax.rs index 0d130be4..a2ba015e 100644 --- a/tests/custom_syntax.rs +++ b/tests/custom_syntax.rs @@ -6,7 +6,7 @@ use rhai::{ fn test_custom_syntax() -> Result<(), Box> { 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"); @@ -79,7 +79,7 @@ fn test_custom_syntax() -> Result<(), Box> { assert!(matches!( *engine - .consume("let foo = (exec [x<<15] -> { x += 2 } while x < 42) * 10;") + .run("let foo = (exec [x<<15] -> { x += 2 } while x < 42) * 10;") .expect_err("should error"), EvalAltResult::ErrorRuntime(_, _) )); diff --git a/tests/data_size.rs b/tests/data_size.rs index aa92c91b..303496f8 100644 --- a/tests/data_size.rs +++ b/tests/data_size.rs @@ -208,7 +208,7 @@ fn test_max_map_size() -> Result<(), Box> { assert!(matches!( *engine - .consume( + .run( " let x = #{}; loop { x.a = x; } diff --git a/tests/modules.rs b/tests/modules.rs index b55e275d..8a6f8d67 100644 --- a/tests/modules.rs +++ b/tests/modules.rs @@ -368,7 +368,7 @@ fn test_module_from_ast() -> Result<(), Box> { ); 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> { static_modules.insert("test_module", module); engine.set_module_resolver(static_modules); - engine.consume(SCRIPT)?; + engine.run(SCRIPT)?; Ok(()) } diff --git a/tests/plugins.rs b/tests/plugins.rs index 9e5548f6..40e2a31a 100644 --- a/tests/plugins.rs +++ b/tests/plugins.rs @@ -87,11 +87,11 @@ fn test_plugins_package() -> Result<(), Box> { #[cfg(not(feature = "no_object"))] { assert_eq!(engine.eval::("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") ) } diff --git a/tests/print.rs b/tests/print.rs index 0bfcefcf..ac7dbcef 100644 --- a/tests/print.rs +++ b/tests/print.rs @@ -50,10 +50,10 @@ fn test_print_debug() -> Result<(), Box> { }); // 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> { .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!( diff --git a/tests/side_effects.rs b/tests/side_effects.rs index 6f729ded..ad5a8f42 100644 --- a/tests/side_effects.rs +++ b/tests/side_effects.rs @@ -74,7 +74,7 @@ fn test_side_effects_print() -> Result<(), Box> { 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(()) diff --git a/tests/while_loop.rs b/tests/while_loop.rs index 7507242b..4e18a371 100644 --- a/tests/while_loop.rs +++ b/tests/while_loop.rs @@ -57,9 +57,9 @@ fn test_infinite_loops() -> Result<(), Box> { 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(()) } From 06f217d5260fb91592b413d1117c062249a3224f Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 6 Aug 2021 14:46:30 +0800 Subject: [PATCH 08/19] Move deprecated functions into separate file. --- src/deprecated.rs | 143 ++++++++++++++++++++++++++++++++++++++++++++++ src/dynamic.rs | 27 --------- src/engine_api.rs | 98 ------------------------------- src/lib.rs | 1 + 4 files changed, 144 insertions(+), 125 deletions(-) create mode 100644 src/deprecated.rs diff --git a/src/deprecated.rs b/src/deprecated.rs new file mode 100644 index 00000000..407f9630 --- /dev/null +++ b/src/deprecated.rs @@ -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> { + 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> { + 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> { + 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> { + 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> { + 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> { + 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 { + 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 { + self.into_immutable_string() + } +} diff --git a/src/dynamic.rs b/src/dynamic.rs index 15862f6a..86eb0487 100644 --- a/src/dynamic.rs +++ b/src/dynamic.rs @@ -1900,20 +1900,6 @@ 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 { - self.into_string() - } - /// 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(always)] pub fn into_string(self) -> Result { self.into_immutable_string() @@ -1921,19 +1907,6 @@ impl Dynamic { } /// 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 { - self.into_immutable_string() - } - /// Convert the [`Dynamic`] into an [`ImmutableString`] and return it. - /// Returns the name of the actual type if the cast fails. #[inline] pub fn into_immutable_string(self) -> Result { match self.0 { diff --git a/src/engine_api.rs b/src/engine_api.rs index 00e531a8..fd487c82 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -1741,23 +1741,6 @@ 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. - /// - /// 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> { - self.run_file(path) - } /// Evaluate a file, returning any error (if any). /// /// Not available under `no_std` or `WASM`. @@ -1767,27 +1750,6 @@ impl Engine { pub fn run_file(&self, path: std::path::PathBuf) -> Result<(), Box> { 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. - /// - /// 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> { - self.run_file_with_scope(scope, path) - } /// Evaluate a file with own scope, returning any error (if any). /// /// Not available under `no_std` or `WASM`. @@ -1801,41 +1763,11 @@ impl Engine { ) -> Result<(), Box> { 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. - /// - /// # 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> { - self.run(script) - } /// Evaluate a script, returning any error (if any). #[inline(always)] pub fn run(&self, script: &str) -> Result<(), Box> { 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. - /// - /// # 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> { - self.run_with_scope(scope, script) - } /// Evaluate a script with own scope, returning any error (if any). #[inline] pub fn run_with_scope( @@ -1856,41 +1788,11 @@ impl Engine { 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. - /// - /// # 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> { - self.run_ast(ast) - } /// Evaluate an AST, returning any error (if any). #[inline(always)] pub fn run_ast(&self, ast: &AST) -> Result<(), Box> { 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. - /// - /// # 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> { - self.run_ast_with_scope(scope, ast) - } /// Evaluate an [`AST`] with own scope, returning any error (if any). #[inline] pub fn run_ast_with_scope( diff --git a/src/lib.rs b/src/lib.rs index 614a8fb8..1466bbb7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,6 +71,7 @@ use std::prelude::v1::*; mod ast; mod custom_syntax; +mod deprecated; mod dynamic; mod engine; mod engine_api; From 288d5750468064e0f998b30c27e266f3cc97a417 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 11 Aug 2021 18:15:17 +0800 Subject: [PATCH 09/19] Add log10 for Decimal. --- CHANGELOG.md | 2 ++ Cargo.toml | 2 +- src/packages/math_basic.rs | 19 +++++++++++++++++-- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 013025aa..8b8e8aac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ Enhancements * 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. Version 1.0.2 diff --git a/Cargo.toml b/Cargo.toml index 701fdd60..cd52b2c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 diff --git a/src/packages/math_basic.rs b/src/packages/math_basic.rs index 53c0431b..f3c9c5ea 100644 --- a/src/packages/math_basic.rs +++ b/src/packages/math_basic.rs @@ -339,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> { + 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> { + 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 { From 4c658bc93228552f4240eb1ce0e8f5df132d4624 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 11 Aug 2021 23:16:13 +0800 Subject: [PATCH 10/19] Bump version to 1.1.0. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index cd52b2c9..bf0a805f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" From b915433b000505d6e9f827b4a96cf23e5315be02 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 12 Aug 2021 11:28:31 +0800 Subject: [PATCH 11/19] Change build config to v1.1. --- .github/workflows/benchmark.yml | 2 +- .github/workflows/build.yml | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index df310705..6720efb5 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -2,7 +2,7 @@ name: Benchmark on: push: branches: - - master + - v1.1 jobs: benchmark: diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 24607668..43f515e5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,8 +3,7 @@ name: Build on: push: branches: - - master - - plugins + - v1.1 pull_request: {} jobs: From c1b4c81e751ecd39938124b8f08fc39e98cb349f Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 13 Aug 2021 19:23:05 +0800 Subject: [PATCH 12/19] Scope::set_value takes name that implements Into>. --- src/scope.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/scope.rs b/src/scope.rs index 823d640d..8e6fccb6 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -362,12 +362,16 @@ impl<'a> Scope<'a> { /// assert_eq!(my_scope.get_value::("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 + Into>, + 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 From 114c93f43049b05d3891e34bad752a0795abbc05 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 13 Aug 2021 19:23:20 +0800 Subject: [PATCH 13/19] Fix bug in custom syntax starting with disabled stardard keyword. --- CHANGELOG.md | 6 ++++++ src/custom_syntax.rs | 19 +++++++++---------- tests/custom_syntax.rs | 38 +++++++++++++++++++++++++++++++++++++- 3 files changed, 52 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b8e8aac..37515af0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ Rhai Release Notes Version 1.1.0 ============= +Bug fixes +--------- + +* Custom syntax starting with a disabled standard keyword now works properly. + Enhancements ------------ @@ -16,6 +21,7 @@ Enhancements * `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>`. Version 1.0.2 diff --git a/src/custom_syntax.rs b/src/custom_syntax.rs index 5325d98b..be4a22e7 100644 --- a/src/custom_syntax.rs +++ b/src/custom_syntax.rs @@ -230,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_standard_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(), @@ -247,13 +246,13 @@ 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()) { - self.custom_keywords.insert(s.into(), None); + if !self.custom_keywords.contains_key(s) { + self.custom_keywords.insert(s.into(), None); + } } s.into() } diff --git a/tests/custom_syntax.rs b/tests/custom_syntax.rs index a2ba015e..32832332 100644 --- a/tests/custom_syntax.rs +++ b/tests/custom_syntax.rs @@ -1,5 +1,5 @@ use rhai::{ - Dynamic, Engine, EvalAltResult, ImmutableString, LexError, ParseErrorType, Position, INT, + Dynamic, Engine, EvalAltResult, ImmutableString, LexError, ParseErrorType, Position, Scope, INT, }; #[test] @@ -146,6 +146,42 @@ fn test_custom_syntax() -> Result<(), Box> { assert_eq!(engine.eval::("test2 } 42")?, 42); assert_eq!(engine.eval::("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)?; + + // Push new variable into the scope if it doesn't already exist. + // Otherwise set its value. + // WARNING - This panics if 'var_name' already exists and is constant! + // - In a real implementation, check this before doing anything! + context.scope_mut().set_value(var_name, value); + + Ok(Dynamic::UNIT) + }, + )?; + + let mut scope = Scope::new(); + + assert_eq!( + engine.eval_with_scope::(&mut scope, "var foo = 42; foo")?, + 42 + ); + assert_eq!(scope.get_value::("foo"), Some(42)); + assert_eq!(scope.len(), 1); + assert_eq!( + engine.eval_with_scope::(&mut scope, "var foo = 123; foo")?, + 123 + ); + assert_eq!(scope.get_value::("foo"), Some(123)); + assert_eq!(scope.len(), 1); + Ok(()) } From 0354f154cef8540d1bb516e31e9550f52cd216e1 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 13 Aug 2021 22:47:03 +0800 Subject: [PATCH 14/19] Add Scope::is_constant. --- CHANGELOG.md | 1 + src/scope.rs | 24 ++++++++++++++++++++++++ tests/custom_syntax.rs | 13 ++++++------- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37515af0..dbfdbdb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ Enhancements * Added `log10()` for `Decimal`. * `ln` for `Decimal` is now checked and won't panic. * `Scope::set_value` now takes anything that implements `Into>`. +* Added `Scope::is_constant` to check if a variable is constant. Version 1.0.2 diff --git a/src/scope.rs b/src/scope.rs index 8e6fccb6..33bf4600 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -339,6 +339,30 @@ 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 { + 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`]. /// /// Search starts backwards from the last, and only the first entry matching the specified name is updated. diff --git a/tests/custom_syntax.rs b/tests/custom_syntax.rs index 32832332..f1b5a297 100644 --- a/tests/custom_syntax.rs +++ b/tests/custom_syntax.rs @@ -157,13 +157,12 @@ fn test_custom_syntax() -> Result<(), Box> { // Evaluate the expression let value = context.eval_expression_tree(expr)?; - // Push new variable into the scope if it doesn't already exist. - // Otherwise set its value. - // WARNING - This panics if 'var_name' already exists and is constant! - // - In a real implementation, check this before doing anything! - context.scope_mut().set_value(var_name, value); - - Ok(Dynamic::UNIT) + 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()) + } }, )?; From 9c6cea3c9f827e1dc32340f1144c24569fa98ffd Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 14 Aug 2021 14:56:15 +0800 Subject: [PATCH 15/19] Add Scope::set_or_push. --- CHANGELOG.md | 1 + src/scope.rs | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbfdbdb6..93f20647 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ Enhancements * `ln` for `Decimal` is now checked and won't panic. * `Scope::set_value` now takes anything that implements `Into>`. * 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 diff --git a/src/scope.rs b/src/scope.rs index 33bf4600..cb13ccab 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -363,6 +363,51 @@ impl<'a> Scope<'a> { 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::("x").unwrap(), 42); + /// assert_eq!(my_scope.len(), 1); + /// + /// my_scope.set_or_push("x", 0_i64); + /// assert_eq!(my_scope.get_value::("x").unwrap(), 0); + /// assert_eq!(my_scope.len(), 1); + /// + /// my_scope.set_or_push("y", 123_i64); + /// assert_eq!(my_scope.get_value::("y").unwrap(), 123); + /// assert_eq!(my_scope.len(), 2); + /// ``` + #[inline] + pub fn set_or_push( + &mut self, + name: impl AsRef + Into>, + 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. From b6eac65601d60f39f25d6b7ff6ebeb5f08da252e Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 17 Aug 2021 18:14:16 +0800 Subject: [PATCH 16/19] Simplify code. --- src/parse.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/parse.rs b/src/parse.rs index b0656b44..17ddde41 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -1683,15 +1683,9 @@ fn make_dot_expr( op_pos: Position, ) -> Result { 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 From 58352e00bd7289846bfd19edcb296fad79ca1d0f Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 17 Aug 2021 23:24:41 +0800 Subject: [PATCH 17/19] Prevent spill-over of new variables in Engine::call_fn. --- CHANGELOG.md | 1 + src/engine_api.rs | 3 +++ 2 files changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93f20647..c12e8938 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ 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 ------------ diff --git a/src/engine_api.rs b/src/engine_api.rs index fd487c82..181e59d8 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -1979,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 From de674b99e9d059468fc96ddae6640377a0814809 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 21 Aug 2021 10:09:32 +0800 Subject: [PATCH 18/19] Revise CI. --- .github/workflows/benchmark.yml | 2 +- .github/workflows/build.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 6720efb5..df310705 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -2,7 +2,7 @@ name: Benchmark on: push: branches: - - v1.1 + - master jobs: benchmark: diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 43f515e5..01204a70 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,7 +3,7 @@ name: Build on: push: branches: - - v1.1 + - master pull_request: {} jobs: From 33d1006d28dfe71e66fc48c1c0d9ece92605aa77 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 21 Aug 2021 11:51:45 +0800 Subject: [PATCH 19/19] Fix test output. --- .github/workflows/build.yml | 1 + codegen/ui_tests/rhai_fn_non_clonable_return.stderr | 7 ++++--- codegen/ui_tests/rhai_mod_non_clonable_return.stderr | 7 ++++--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 01204a70..8e97e90a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,6 +4,7 @@ on: push: branches: - master + - bug-fixes pull_request: {} jobs: diff --git a/codegen/ui_tests/rhai_fn_non_clonable_return.stderr b/codegen/ui_tests/rhai_fn_non_clonable_return.stderr index 28d43e99..f5a3d29c 100644 --- a/codegen/ui_tests/rhai_fn_non_clonable_return.stderr +++ b/codegen/ui_tests/rhai_fn_non_clonable_return.stderr @@ -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(mut value: T) -> Self { - | ----- required by this bound in `rhai::Dynamic::from` +1127 | pub fn from(mut value: T) -> Self { + | ^^^^^ required by this bound in `rhai::Dynamic::from` diff --git a/codegen/ui_tests/rhai_mod_non_clonable_return.stderr b/codegen/ui_tests/rhai_mod_non_clonable_return.stderr index 73ba7569..17e1ae0e 100644 --- a/codegen/ui_tests/rhai_mod_non_clonable_return.stderr +++ b/codegen/ui_tests/rhai_mod_non_clonable_return.stderr @@ -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(mut value: T) -> Self { - | ----- required by this bound in `rhai::Dynamic::from` +1127 | pub fn from(mut value: T) -> Self { + | ^^^^^ required by this bound in `rhai::Dynamic::from`