diff --git a/CHANGELOG.md b/CHANGELOG.md index ac70de93..0986d3b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,20 @@ New features * Added support for integer _ranges_ via the `..` and `..=` operators. +Version 1.3.1 +============= + +Bug fixes +--------- + +* Custom syntax now works properly inside binary expressions. + +Enhancements +------------ + +* `BLOB`'s are refined to display in a more compact hex format. + + Version 1.3.0 ============= @@ -105,7 +119,7 @@ Deprecated API's ---------------- * `NativeCallContext::call_fn_dynamic_raw` is deprecated and `NativeCallContext::call_fn_raw` is added. -* `From` for `Result>` is deprecated so it will no longer be possible to do `EvalAltResult::ErrorXXXXX.into()` to convert to a `Result`; instead, `Err(EvalAltResult:ErrorXXXXX.into())` must be used. Code is clearer if errors are explicitly wrapped in `Err`. +* `From` for `Result >` is deprecated so it will no longer be possible to do `EvalAltResult::ErrorXXXXX.into()` to convert to a `Result`; instead, `Err(EvalAltResult:ErrorXXXXX.into())` must be used. Code is clearer if errors are explicitly wrapped in `Err`. Version 1.1.2 @@ -178,7 +192,7 @@ Enhancements ### `Scope` API -* `Scope::set_value` now takes anything that implements `Into>`. +* `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. @@ -463,7 +477,8 @@ Enhancements * Replaced all `HashMap` usage with `BTreeMap` for better performance because collections in Rhai are tiny. * `Engine::register_result_fn` no longer requires the successful return type to be `Dynamic`. It can now be any clonable type. -* `#[rhai_fn(return_raw)]` can now return `Result>` where `T` is any clonable type instead of `Result>`. +* `#[rhai_fn(return_raw)]` can now return `Result >` where `T` is any clonable + type instead of `Result >`. * `Dynamic::clone_cast` is added to simplify casting from a `&Dynamic`. @@ -933,7 +948,7 @@ Breaking changes ---------------- * `AST::iter_functions` now returns an iterator instead of taking a closure. -* `Module::get_script_function_by_signature` renamed to `Module::get_script_fn` and returns `&>`. +* `Module::get_script_function_by_signature` renamed to `Module::get_script_fn` and returns `& >`. * `Module::num_fn`, `Module::num_var` and `Module::num_iter` are removed and merged into `Module::count`. * The `merge_namespaces` parameter to `Module::eval_ast_as_new` is removed and now defaults to `true`. * `GlobalFileModuleResolver` is removed because its performance gain over the `FileModuleResolver` is no longer very significant. @@ -981,7 +996,7 @@ Bug fixes Breaking changes ---------------- -* `Engine::register_set_result` and `Engine::register_indexer_set_result` now take a function that returns `Result<(), Box>`. +* `Engine::register_set_result` and `Engine::register_indexer_set_result` now take a function that returns `Result<(), Box >`. * `Engine::register_indexer_XXX` and `Module::set_indexer_XXX` panic when the type is `Array`, `Map` or `String`. * `EvalAltResult` has a new variant `ErrorInModule` which holds errors when loading an external module. * `Module::eval_ast_as_new` now takes an extra boolean parameter, indicating whether to encapsulate the entire module into a separate namespace. @@ -1102,7 +1117,7 @@ Breaking changes * `EvalAltResult::ErrorMismatchOutputType` has an extra argument containing the name of the requested type. * `Engine::call_fn_dynamic` take an extra argument, allowing a `Dynamic` value to be bound to the `this` pointer. -* Precedence of the `%` (modulo) operator is lowered to below `<<` ad `>>`. This is to handle the case of `x << 3 % 10`. +* Precedence of the `%` (modulo) operator is lowered to below bit shifts. This is to handle the case of `x < < 3 % 10`. New features ------------ diff --git a/README.md b/README.md index 92248da0..85dac181 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ The [`scripts`](https://github.com/rhaiscript/rhai/tree/master/scripts) subdirec Below is the standard _Fibonacci_ example for scripting languages: -```js +```ts // This Rhai script calculates the n-th Fibonacci number using a // really dumb algorithm to test the speed of the scripting engine. diff --git a/src/parser.rs b/src/parser.rs index 1698aa25..757001fb 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1570,9 +1570,26 @@ fn parse_unary( settings.ensure_level_within_max_limit(state.max_expr_depth)?; let (token, token_pos) = input.peek().expect(NEVER_ENDS); + let token_pos = *token_pos; let mut settings = settings; - settings.pos = *token_pos; + settings.pos = token_pos; + + // Check if it is a custom syntax. + if !state.engine.custom_syntax.is_empty() { + match token { + Token::Custom(key) | Token::Reserved(key) | Token::Identifier(key) => { + if let Some((key, syntax)) = state.engine.custom_syntax.get_key_value(key.as_ref()) + { + input.next().expect(NEVER_ENDS); + return parse_custom_syntax( + input, state, lib, settings, key, syntax, token_pos, + ); + } + } + _ => (), + } + } match token { // -expr @@ -2259,25 +2276,6 @@ fn parse_expr( let mut settings = settings; settings.pos = input.peek().expect(NEVER_ENDS).1; - // Check if it is a custom syntax. - if !state.engine.custom_syntax.is_empty() { - let (token, pos) = input.peek().expect(NEVER_ENDS); - let token_pos = *pos; - - match token { - Token::Custom(key) | Token::Reserved(key) | Token::Identifier(key) => { - if let Some((key, syntax)) = state.engine.custom_syntax.get_key_value(key.as_ref()) - { - input.next().expect(NEVER_ENDS); - return parse_custom_syntax( - input, state, lib, settings, key, syntax, token_pos, - ); - } - } - _ => (), - } - } - // Parse expression normally. let lhs = parse_unary(input, state, lib, settings.level_up())?; parse_binary_op( diff --git a/src/tokenizer.rs b/src/tokenizer.rs index b49cbd68..d00aa2b5 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -883,11 +883,6 @@ impl Token { use Token::*; Precedence::new(match self { - // Assignments are not considered expressions - set to zero - Equals | PlusAssign | MinusAssign | MultiplyAssign | DivideAssign | PowerOfAssign - | LeftShiftAssign | RightShiftAssign | AndAssign | OrAssign | XOrAssign - | ModuloAssign => 0, - Or | XOr | Pipe => 30, And | Ampersand => 60, @@ -908,8 +903,6 @@ impl Token { LeftShift | RightShift => 210, - Period => 240, - _ => 0, }) } @@ -920,14 +913,6 @@ impl Token { use Token::*; match self { - // Assignments bind to the right - Equals | PlusAssign | MinusAssign | MultiplyAssign | DivideAssign | PowerOfAssign - | LeftShiftAssign | RightShiftAssign | AndAssign | OrAssign | XOrAssign - | ModuloAssign => true, - - // Property access binds to the right - Period => true, - // Exponentiation binds to the right PowerOf => true, @@ -1685,6 +1670,17 @@ fn get_next_token_inner( // Shebang ('#', '!') => return Some((Token::Reserved("#!".into()), start_pos)), + ('#', ' ') => { + eat_next(stream, pos); + let token = if stream.peek_next() == Some('{') { + eat_next(stream, pos); + "# {" + } else { + "#" + }; + return Some((Token::Reserved(token.into()), start_pos)); + } + ('#', _) => return Some((Token::Reserved("#".into()), start_pos)), // Operators @@ -2212,7 +2208,7 @@ impl<'a> Iterator for TokenIterator<'a> { ("(*", false) | ("*)", false) => Token::LexError(LERR::ImproperSymbol(s.to_string(), "'(* .. *)' is not a valid comment format. This is not Pascal! Should it be '/* .. */'?".to_string(), )), - ("#", false) => Token::LexError(LERR::ImproperSymbol(s.to_string(), + ("# {", false) => Token::LexError(LERR::ImproperSymbol(s.to_string(), "'#' is not a valid symbol. Should it be '#{'?".to_string(), )), // Reserved keyword/operator that is custom. diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index 8186f4bd..0c056d81 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -703,7 +703,16 @@ impl fmt::Debug for Dynamic { #[cfg(not(feature = "no_index"))] Union::Array(ref value, _, _) => fmt::Debug::fmt(value, f), #[cfg(not(feature = "no_index"))] - Union::Blob(ref value, _, _) => fmt::Debug::fmt(value, f), + Union::Blob(ref value, _, _) => { + f.write_str("[")?; + value.iter().enumerate().try_for_each(|(i, v)| { + if i > 0 && i % 8 == 0 { + f.write_str(" ")?; + } + write!(f, "{:02x}", v) + })?; + f.write_str("]") + } #[cfg(not(feature = "no_object"))] Union::Map(ref value, _, _) => { f.write_str("#")?; diff --git a/tests/custom_syntax.rs b/tests/custom_syntax.rs index f381bdf0..cbc9962a 100644 --- a/tests/custom_syntax.rs +++ b/tests/custom_syntax.rs @@ -239,3 +239,37 @@ fn test_custom_syntax_raw() -> Result<(), Box> { Ok(()) } + +#[test] +fn test_custom_syntax_raw2() -> Result<(), Box> { + let mut engine = Engine::new(); + + engine + .register_custom_operator("#", 255)? + .register_custom_syntax_raw( + "#", + |symbols, lookahead| match symbols.len() { + 1 if lookahead == "-" => Ok(Some("$symbol$".into())), + 1 => Ok(Some("$int$".into())), + 2 if symbols[1] == "-" => Ok(Some("$int$".into())), + 2 => Ok(None), + 3 => Ok(None), + _ => unreachable!(), + }, + false, + move |_, inputs| { + let id = if inputs.len() == 2 { + -inputs[1].get_literal_value::().unwrap() + } else { + inputs[0].get_literal_value::().unwrap() + }; + Ok(id.into()) + }, + ); + + assert_eq!(engine.eval::("let x = 41; x + #1")?, 42); + assert_eq!(engine.eval::("#42/2")?, 21); + assert_eq!(engine.eval::("#-1")?, -1); + + Ok(()) +} diff --git a/tests/serde.rs b/tests/serde.rs index 32b0c64e..5e20f04a 100644 --- a/tests/serde.rs +++ b/tests/serde.rs @@ -12,6 +12,7 @@ use rhai::Array; use rhai::Map; #[cfg(not(feature = "no_float"))] use rhai::FLOAT; +#[cfg(not(feature = "no_float"))] #[cfg(feature = "decimal")] use rust_decimal::Decimal;