diff --git a/CHANGELOG.md b/CHANGELOG.md index c0b20648..8c60ac64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ Rhai Release Notes Version 1.3.1 ============= +Bug fixes +--------- + +* Custom syntax now works properly inside binary expressions. + Enhancements ------------ diff --git a/README.md b/README.md index 07a221a4..bfda54e5 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 25bc5272..600f0090 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1550,9 +1550,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 @@ -2251,25 +2268,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 c039a8dc..e5a4905d 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -863,11 +863,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, @@ -886,8 +881,6 @@ impl Token { LeftShift | RightShift => 210, - Period => 240, - _ => 0, }) } @@ -898,14 +891,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, @@ -1662,6 +1647,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 @@ -2182,7 +2178,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/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 25929ac5..b1b76e3a 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;