Merge branch 'v1.3-fixes'
This commit is contained in:
commit
f92cbe1f6d
27
CHANGELOG.md
27
CHANGELOG.md
@ -13,6 +13,20 @@ New features
|
|||||||
* Added support for integer _ranges_ via the `..` and `..=` operators.
|
* 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
|
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.
|
* `NativeCallContext::call_fn_dynamic_raw` is deprecated and `NativeCallContext::call_fn_raw` is added.
|
||||||
* `From<EvalAltResult>` for `Result<T, Box<EvalAltResult>>` 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<EvalAltResult>` for `Result<T, Box<EvalAltResult> >` 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
|
Version 1.1.2
|
||||||
@ -178,7 +192,7 @@ Enhancements
|
|||||||
|
|
||||||
### `Scope` API
|
### `Scope` API
|
||||||
|
|
||||||
* `Scope::set_value` now takes anything that implements `Into<Cow<str>>`.
|
* `Scope::set_value` now takes anything that implements `Into<Cow<str> >`.
|
||||||
* Added `Scope::is_constant` to check if a variable is constant.
|
* 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.
|
* 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.
|
* 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.
|
* `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<T, Box<EvalAltResult>>` where `T` is any clonable type instead of `Result<Dynamic, Box<EvalAltResult>>`.
|
* `#[rhai_fn(return_raw)]` can now return `Result<T, Box<EvalAltResult> >` where `T` is any clonable
|
||||||
|
type instead of `Result<Dynamic, Box<EvalAltResult> >`.
|
||||||
* `Dynamic::clone_cast` is added to simplify casting from a `&Dynamic`.
|
* `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.
|
* `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 `&<Shared<ScriptFnDef>>`.
|
* `Module::get_script_function_by_signature` renamed to `Module::get_script_fn` and returns `&<Shared<ScriptFnDef> >`.
|
||||||
* `Module::num_fn`, `Module::num_var` and `Module::num_iter` are removed and merged into `Module::count`.
|
* `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`.
|
* 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.
|
* `GlobalFileModuleResolver` is removed because its performance gain over the `FileModuleResolver` is no longer very significant.
|
||||||
@ -981,7 +996,7 @@ Bug fixes
|
|||||||
Breaking changes
|
Breaking changes
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
* `Engine::register_set_result` and `Engine::register_indexer_set_result` now take a function that returns `Result<(), Box<EvalAltResult>>`.
|
* `Engine::register_set_result` and `Engine::register_indexer_set_result` now take a function that returns `Result<(), Box<EvalAltResult> >`.
|
||||||
* `Engine::register_indexer_XXX` and `Module::set_indexer_XXX` panic when the type is `Array`, `Map` or `String`.
|
* `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.
|
* `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.
|
* `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.
|
* `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.
|
* `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
|
New features
|
||||||
------------
|
------------
|
||||||
|
@ -79,7 +79,7 @@ The [`scripts`](https://github.com/rhaiscript/rhai/tree/master/scripts) subdirec
|
|||||||
|
|
||||||
Below is the standard _Fibonacci_ example for scripting languages:
|
Below is the standard _Fibonacci_ example for scripting languages:
|
||||||
|
|
||||||
```js
|
```ts
|
||||||
// This Rhai script calculates the n-th Fibonacci number using a
|
// This Rhai script calculates the n-th Fibonacci number using a
|
||||||
// really dumb algorithm to test the speed of the scripting engine.
|
// really dumb algorithm to test the speed of the scripting engine.
|
||||||
|
|
||||||
|
@ -1570,9 +1570,26 @@ fn parse_unary(
|
|||||||
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
||||||
|
|
||||||
let (token, token_pos) = input.peek().expect(NEVER_ENDS);
|
let (token, token_pos) = input.peek().expect(NEVER_ENDS);
|
||||||
|
let token_pos = *token_pos;
|
||||||
|
|
||||||
let mut settings = settings;
|
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 {
|
match token {
|
||||||
// -expr
|
// -expr
|
||||||
@ -2259,25 +2276,6 @@ fn parse_expr(
|
|||||||
let mut settings = settings;
|
let mut settings = settings;
|
||||||
settings.pos = input.peek().expect(NEVER_ENDS).1;
|
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.
|
// Parse expression normally.
|
||||||
let lhs = parse_unary(input, state, lib, settings.level_up())?;
|
let lhs = parse_unary(input, state, lib, settings.level_up())?;
|
||||||
parse_binary_op(
|
parse_binary_op(
|
||||||
|
@ -883,11 +883,6 @@ impl Token {
|
|||||||
use Token::*;
|
use Token::*;
|
||||||
|
|
||||||
Precedence::new(match self {
|
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,
|
Or | XOr | Pipe => 30,
|
||||||
|
|
||||||
And | Ampersand => 60,
|
And | Ampersand => 60,
|
||||||
@ -908,8 +903,6 @@ impl Token {
|
|||||||
|
|
||||||
LeftShift | RightShift => 210,
|
LeftShift | RightShift => 210,
|
||||||
|
|
||||||
Period => 240,
|
|
||||||
|
|
||||||
_ => 0,
|
_ => 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -920,14 +913,6 @@ impl Token {
|
|||||||
use Token::*;
|
use Token::*;
|
||||||
|
|
||||||
match self {
|
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
|
// Exponentiation binds to the right
|
||||||
PowerOf => true,
|
PowerOf => true,
|
||||||
|
|
||||||
@ -1685,6 +1670,17 @@ fn get_next_token_inner(
|
|||||||
// Shebang
|
// Shebang
|
||||||
('#', '!') => return Some((Token::Reserved("#!".into()), start_pos)),
|
('#', '!') => 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)),
|
('#', _) => return Some((Token::Reserved("#".into()), start_pos)),
|
||||||
|
|
||||||
// Operators
|
// Operators
|
||||||
@ -2212,7 +2208,7 @@ impl<'a> Iterator for TokenIterator<'a> {
|
|||||||
("(*", false) | ("*)", false) => Token::LexError(LERR::ImproperSymbol(s.to_string(),
|
("(*", false) | ("*)", false) => Token::LexError(LERR::ImproperSymbol(s.to_string(),
|
||||||
"'(* .. *)' is not a valid comment format. This is not Pascal! Should it be '/* .. */'?".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(),
|
"'#' is not a valid symbol. Should it be '#{'?".to_string(),
|
||||||
)),
|
)),
|
||||||
// Reserved keyword/operator that is custom.
|
// Reserved keyword/operator that is custom.
|
||||||
|
@ -703,7 +703,16 @@ impl fmt::Debug for Dynamic {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Union::Array(ref value, _, _) => fmt::Debug::fmt(value, f),
|
Union::Array(ref value, _, _) => fmt::Debug::fmt(value, f),
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[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"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(ref value, _, _) => {
|
Union::Map(ref value, _, _) => {
|
||||||
f.write_str("#")?;
|
f.write_str("#")?;
|
||||||
|
@ -239,3 +239,37 @@ fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
Ok(())
|
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;
|
use rhai::Map;
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
use rhai::FLOAT;
|
use rhai::FLOAT;
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
use rust_decimal::Decimal;
|
use rust_decimal::Decimal;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user