No need to specify number of variables added/removed for custom syntax.
This commit is contained in:
parent
3a47ed7c46
commit
97c8194d17
@ -8,6 +8,7 @@ Breaking changes
|
|||||||
----------------
|
----------------
|
||||||
|
|
||||||
* `Engine::disable_doc_comments` is removed because doc-comments are now placed under the `metadata` feature flag.
|
* `Engine::disable_doc_comments` is removed because doc-comments are now placed under the `metadata` feature flag.
|
||||||
|
* Registering a custom syntax now only requires specifying whether the `Scope` is adjusted (i.e. whether variables are added or removed). There is no need to specify the number of variables added/removed.
|
||||||
|
|
||||||
New features
|
New features
|
||||||
------------
|
------------
|
||||||
@ -16,6 +17,11 @@ New features
|
|||||||
* A new internal feature `no_smartstring` to turn off `SmartString` for those rare cases that it is needed.
|
* 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`.
|
* `DynamicReadLock` and `DynamicWriteLoc` are exposed under `internals`.
|
||||||
|
|
||||||
|
Enhancements
|
||||||
|
------------
|
||||||
|
|
||||||
|
* Registering a custom syntax now only requires specifying whether the `Scope` is adjusted (i.e. whether variables are added or removed). This allows more flexibility for cases where the number of new variables declared depends on internal logic.
|
||||||
|
|
||||||
|
|
||||||
Version 0.20.1
|
Version 0.20.1
|
||||||
==============
|
==============
|
||||||
|
@ -1357,8 +1357,8 @@ impl Stmt {
|
|||||||
pub struct CustomExpr {
|
pub struct CustomExpr {
|
||||||
/// List of keywords.
|
/// List of keywords.
|
||||||
pub keywords: StaticVec<Expr>,
|
pub keywords: StaticVec<Expr>,
|
||||||
/// Delta number of variables in the scope.
|
/// Is the current [`Scope`][crate::Scope] modified?
|
||||||
pub scope_delta: isize,
|
pub scope_changed: bool,
|
||||||
/// List of tokens actually parsed.
|
/// List of tokens actually parsed.
|
||||||
pub tokens: StaticVec<Identifier>,
|
pub tokens: StaticVec<Identifier>,
|
||||||
}
|
}
|
||||||
|
@ -1024,7 +1024,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
|
|||||||
|
|
||||||
// Custom syntax
|
// Custom syntax
|
||||||
Expr::Custom(x, _) => {
|
Expr::Custom(x, _) => {
|
||||||
if x.scope_delta != 0 {
|
if x.scope_changed {
|
||||||
state.propagate_constants = false;
|
state.propagate_constants = false;
|
||||||
}
|
}
|
||||||
x.keywords.iter_mut().for_each(|expr| optimize_expr(expr, state));
|
x.keywords.iter_mut().for_each(|expr| optimize_expr(expr, state));
|
||||||
|
@ -1848,22 +1848,12 @@ fn parse_custom_syntax(
|
|||||||
let mut tokens: StaticVec<_> = Default::default();
|
let mut tokens: StaticVec<_> = Default::default();
|
||||||
|
|
||||||
// Adjust the variables stack
|
// Adjust the variables stack
|
||||||
match syntax.scope_delta {
|
if syntax.scope_changed {
|
||||||
delta if delta > 0 => {
|
// Add an empty variable name to the stack.
|
||||||
// Add enough empty variable names to the stack.
|
|
||||||
// Empty variable names act as a barrier so earlier variables will not be matched.
|
// Empty variable names act as a barrier so earlier variables will not be matched.
|
||||||
// Variable searches stop at the first empty variable name.
|
// Variable searches stop at the first empty variable name.
|
||||||
let empty = state.get_identifier("");
|
let empty = state.get_identifier("");
|
||||||
state.stack.resize(
|
state.stack.push((empty, AccessMode::ReadWrite));
|
||||||
state.stack.len() + delta as usize,
|
|
||||||
(empty, AccessMode::ReadWrite),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
delta if delta < 0 && state.stack.len() <= delta.abs() as usize => state.stack.clear(),
|
|
||||||
delta if delta < 0 => state
|
|
||||||
.stack
|
|
||||||
.truncate(state.stack.len() - delta.abs() as usize),
|
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let parse_func = &syntax.parse;
|
let parse_func = &syntax.parse;
|
||||||
@ -1936,7 +1926,7 @@ fn parse_custom_syntax(
|
|||||||
Box::new(CustomExpr {
|
Box::new(CustomExpr {
|
||||||
keywords,
|
keywords,
|
||||||
tokens,
|
tokens,
|
||||||
scope_delta: syntax.scope_delta,
|
scope_changed: syntax.scope_changed,
|
||||||
}),
|
}),
|
||||||
pos,
|
pos,
|
||||||
))
|
))
|
||||||
|
@ -87,28 +87,33 @@ pub struct CustomSyntax {
|
|||||||
pub parse: Box<FnCustomSyntaxParse>,
|
pub parse: Box<FnCustomSyntaxParse>,
|
||||||
/// Custom syntax implementation function.
|
/// Custom syntax implementation function.
|
||||||
pub func: Shared<FnCustomSyntaxEval>,
|
pub func: Shared<FnCustomSyntaxEval>,
|
||||||
/// Delta number of variables in the scope.
|
/// Any variables added/removed in the scope?
|
||||||
pub scope_delta: isize,
|
pub scope_changed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Engine {
|
impl Engine {
|
||||||
/// Register a custom syntax with the [`Engine`].
|
/// Register a custom syntax with the [`Engine`].
|
||||||
///
|
///
|
||||||
/// * `keywords` holds a slice of strings that define the custom syntax.
|
/// * `keywords` holds a slice of strings that define the custom syntax.
|
||||||
/// * `new_vars` is the number of new variables declared by this custom syntax, or the number of variables removed (if negative).
|
/// * `scope_changed` specifies variables have been added/removed by this custom syntax.
|
||||||
/// * `func` is the implementation function.
|
/// * `func` is the implementation function.
|
||||||
///
|
///
|
||||||
/// # Notes
|
/// # Caveat - Do not change beyond block scope
|
||||||
///
|
///
|
||||||
/// If `new_vars` is positive, then a number of new variables are expected to be pushed into the
|
/// If `scope_changed` is `true`, then the current [`Scope`][crate::Scope] is assumed to be
|
||||||
/// current [`Scope`][crate::Scope].
|
/// modified by this custom syntax.
|
||||||
///
|
///
|
||||||
/// If `new_vars` is negative, then it is expected that only the top `new_vars` variables in the
|
/// Adding new variables and/or removing variables are allowed. Simply modifying the values of
|
||||||
/// current [`Scope`][crate::Scope] will be _popped_. Do not randomly remove variables.
|
/// variables does NOT count, so `false` should be passed in this case.
|
||||||
|
///
|
||||||
|
/// However, only variables declared within the current _block scope_ should be touched,
|
||||||
|
/// since they all go away at the end of the block.
|
||||||
|
///
|
||||||
|
/// Variables in parent blocks should be left untouched as they persist beyond the current block.
|
||||||
pub fn register_custom_syntax<S: AsRef<str> + Into<Identifier>>(
|
pub fn register_custom_syntax<S: AsRef<str> + Into<Identifier>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
keywords: &[S],
|
keywords: &[S],
|
||||||
new_vars: isize,
|
scope_changed: bool,
|
||||||
func: impl Fn(&mut EvalContext, &[Expression]) -> RhaiResult + SendSync + 'static,
|
func: impl Fn(&mut EvalContext, &[Expression]) -> RhaiResult + SendSync + 'static,
|
||||||
) -> Result<&mut Self, ParseError> {
|
) -> Result<&mut Self, ParseError> {
|
||||||
let keywords = keywords.as_ref();
|
let keywords = keywords.as_ref();
|
||||||
@ -203,7 +208,7 @@ impl Engine {
|
|||||||
Ok(Some(segments[stream.len()].clone()))
|
Ok(Some(segments[stream.len()].clone()))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new_vars,
|
scope_changed,
|
||||||
func,
|
func,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -215,7 +220,7 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// This function is very low level.
|
/// This function is very low level.
|
||||||
///
|
///
|
||||||
/// * `new_vars` is the number of new variables declared by this custom syntax, or the number of variables removed (if negative).
|
/// * `scope_changed` specifies variables have been added/removed by this custom syntax.
|
||||||
/// * `parse` is the parsing function.
|
/// * `parse` is the parsing function.
|
||||||
/// * `func` is the implementation function.
|
/// * `func` is the implementation function.
|
||||||
///
|
///
|
||||||
@ -227,7 +232,7 @@ impl Engine {
|
|||||||
parse: impl Fn(&[ImmutableString], &str) -> Result<Option<ImmutableString>, ParseError>
|
parse: impl Fn(&[ImmutableString], &str) -> Result<Option<ImmutableString>, ParseError>
|
||||||
+ SendSync
|
+ SendSync
|
||||||
+ 'static,
|
+ 'static,
|
||||||
new_vars: isize,
|
scope_changed: bool,
|
||||||
func: impl Fn(&mut EvalContext, &[Expression]) -> RhaiResult + SendSync + 'static,
|
func: impl Fn(&mut EvalContext, &[Expression]) -> RhaiResult + SendSync + 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.custom_syntax.insert(
|
self.custom_syntax.insert(
|
||||||
@ -235,7 +240,7 @@ impl Engine {
|
|||||||
Box::new(CustomSyntax {
|
Box::new(CustomSyntax {
|
||||||
parse: Box::new(parse),
|
parse: Box::new(parse),
|
||||||
func: (Box::new(func) as Box<FnCustomSyntaxEval>).into(),
|
func: (Box::new(func) as Box<FnCustomSyntaxEval>).into(),
|
||||||
scope_delta: new_vars,
|
scope_changed,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
self
|
self
|
||||||
|
@ -19,15 +19,15 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
engine.register_custom_syntax(
|
engine.register_custom_syntax(
|
||||||
&[
|
&[
|
||||||
"exec", "|", "$ident$", "|", "->", "$block$", "while", "$expr$",
|
"exec", "[", "$ident$", "]", "->", "$block$", "while", "$expr$",
|
||||||
],
|
],
|
||||||
1,
|
true,
|
||||||
|context, inputs| {
|
|context, inputs| {
|
||||||
let var_name = inputs[0].get_variable_name().unwrap().to_string();
|
let var_name = inputs[0].get_variable_name().unwrap().to_string();
|
||||||
let stmt = inputs.get(1).unwrap();
|
let stmt = inputs.get(1).unwrap();
|
||||||
let condition = inputs.get(2).unwrap();
|
let condition = inputs.get(2).unwrap();
|
||||||
|
|
||||||
context.scope_mut().push(var_name, 0 as INT);
|
context.scope_mut().push(var_name.clone(), 0 as INT);
|
||||||
|
|
||||||
let mut count: INT = 0;
|
let mut count: INT = 0;
|
||||||
|
|
||||||
@ -35,6 +35,10 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
|||||||
context.eval_expression_tree(stmt)?;
|
context.eval_expression_tree(stmt)?;
|
||||||
count += 1;
|
count += 1;
|
||||||
|
|
||||||
|
context
|
||||||
|
.scope_mut()
|
||||||
|
.push(format!("{}{}", var_name, count), count);
|
||||||
|
|
||||||
let stop = !context
|
let stop = !context
|
||||||
.eval_expression_tree(condition)?
|
.eval_expression_tree(condition)?
|
||||||
.as_bool()
|
.as_bool()
|
||||||
@ -59,7 +63,7 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
|||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
"
|
"
|
||||||
let x = 0;
|
let x = 0;
|
||||||
let foo = (exec |x| -> { x += 2 } while x < 42) * 10;
|
let foo = (exec [x] -> { x += 2 } while x < 42) * 10;
|
||||||
foo
|
foo
|
||||||
"
|
"
|
||||||
)?,
|
)?,
|
||||||
@ -69,7 +73,7 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
|||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
"
|
"
|
||||||
let x = 0;
|
let x = 0;
|
||||||
exec |x| -> { x += 1 } while x < 42;
|
exec [x] -> { x += 1 } while x < 42;
|
||||||
x
|
x
|
||||||
"
|
"
|
||||||
)?,
|
)?,
|
||||||
@ -78,17 +82,27 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
"
|
"
|
||||||
exec |x| -> { x += 1 } while x < 42;
|
exec [x] -> { x += 1 } while x < 42;
|
||||||
x
|
x
|
||||||
"
|
"
|
||||||
)?,
|
)?,
|
||||||
42
|
42
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
"
|
||||||
|
let foo = 123;
|
||||||
|
exec [x] -> { x += 1 } while x < 42;
|
||||||
|
foo + x + x1 + x2 + x3
|
||||||
|
"
|
||||||
|
)?,
|
||||||
|
171
|
||||||
|
);
|
||||||
|
|
||||||
// The first symbol must be an identifier
|
// The first symbol must be an identifier
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*engine
|
*engine
|
||||||
.register_custom_syntax(&["!"], 0, |_, _| Ok(Dynamic::UNIT))
|
.register_custom_syntax(&["!"], false, |_, _| Ok(Dynamic::UNIT))
|
||||||
.expect_err("should error")
|
.expect_err("should error")
|
||||||
.0,
|
.0,
|
||||||
ParseErrorType::BadInput(LexError::ImproperSymbol(
|
ParseErrorType::BadInput(LexError::ImproperSymbol(
|
||||||
@ -121,7 +135,7 @@ fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
|
|||||||
},
|
},
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
1,
|
true,
|
||||||
|context, inputs| {
|
|context, inputs| {
|
||||||
context.scope_mut().push("foo", 999 as INT);
|
context.scope_mut().push("foo", 999 as INT);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user