Allow custom syntax inside binary operator expressions.
This commit is contained in:
parent
93e3c4c10a
commit
224aa0ea68
@ -4,6 +4,11 @@ Rhai Release Notes
|
||||
Version 1.3.1
|
||||
=============
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
|
||||
* Custom syntax now works properly inside binary expressions.
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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.
|
||||
|
@ -239,3 +239,37 @@ fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_custom_syntax_raw2() -> Result<(), Box<EvalAltResult>> {
|
||||
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::<INT>().unwrap()
|
||||
} else {
|
||||
inputs[0].get_literal_value::<INT>().unwrap()
|
||||
};
|
||||
Ok(id.into())
|
||||
},
|
||||
);
|
||||
|
||||
assert_eq!(engine.eval::<INT>("let x = 41; x + #1")?, 42);
|
||||
assert_eq!(engine.eval::<INT>("#42/2")?, 21);
|
||||
assert_eq!(engine.eval::<INT>("#-1")?, -1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user