Allow self-terminating custom syntax.
This commit is contained in:
parent
3127f9a8af
commit
e0125a1033
@ -8,6 +8,7 @@ Enhancements
|
|||||||
------------
|
------------
|
||||||
|
|
||||||
* `$symbol$` is supported in custom syntax to match any symbol.
|
* `$symbol$` is supported in custom syntax to match any symbol.
|
||||||
|
* Custom syntax with `$block$`, `}` or `;` as the last symbol are now self-terminating (i.e. no need to attach a terminating `;`).
|
||||||
* `Dynamic::as_string` and `Dynamic::as_immutable_string` are deprecated and replaced by `into_string` and `into_immutable_string` respectively.
|
* `Dynamic::as_string` and `Dynamic::as_immutable_string` are deprecated and replaced by `into_string` and `into_immutable_string` respectively.
|
||||||
* Added a number of constants to `Dynamic`.
|
* Added a number of constants to `Dynamic`.
|
||||||
* Added a number of constants and `fromXXX` constant methods to `Dynamic`.
|
* Added a number of constants and `fromXXX` constant methods to `Dynamic`.
|
||||||
|
17
src/ast.rs
17
src/ast.rs
@ -1263,10 +1263,12 @@ impl Stmt {
|
|||||||
// A No-op requires a semicolon in order to know it is an empty statement!
|
// A No-op requires a semicolon in order to know it is an empty statement!
|
||||||
Self::Noop(_) => false,
|
Self::Noop(_) => false,
|
||||||
|
|
||||||
|
Self::Expr(Expr::Custom(x, _)) if x.is_self_terminated() => true,
|
||||||
|
|
||||||
Self::Var(_, _, _, _)
|
Self::Var(_, _, _, _)
|
||||||
| Self::Assignment(_, _)
|
| Self::Assignment(_, _)
|
||||||
| Self::FnCall(_, _)
|
|
||||||
| Self::Expr(_)
|
| Self::Expr(_)
|
||||||
|
| Self::FnCall(_, _)
|
||||||
| Self::Do(_, _, _, _)
|
| Self::Do(_, _, _, _)
|
||||||
| Self::Continue(_)
|
| Self::Continue(_)
|
||||||
| Self::Break(_)
|
| Self::Break(_)
|
||||||
@ -1509,6 +1511,19 @@ pub struct CustomExpr {
|
|||||||
pub scope_may_be_changed: bool,
|
pub scope_may_be_changed: bool,
|
||||||
/// List of tokens actually parsed.
|
/// List of tokens actually parsed.
|
||||||
pub tokens: StaticVec<Identifier>,
|
pub tokens: StaticVec<Identifier>,
|
||||||
|
/// Is this custom syntax self-terminated?
|
||||||
|
pub self_terminated: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CustomExpr {
|
||||||
|
/// Is this custom syntax self-terminated (i.e. no need for a semicolon terminator)?
|
||||||
|
///
|
||||||
|
/// A self-terminated custom syntax always ends in `$block$`, `}` or `;`
|
||||||
|
#[must_use]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn is_self_terminated(&self) -> bool {
|
||||||
|
self.self_terminated
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _(internals)_ A binary expression.
|
/// _(internals)_ A binary expression.
|
||||||
|
19
src/parse.rs
19
src/parse.rs
@ -2004,16 +2004,17 @@ fn parse_custom_syntax(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let parse_func = syntax.parse.as_ref();
|
let parse_func = syntax.parse.as_ref();
|
||||||
|
let mut required_token: ImmutableString = key.into();
|
||||||
|
|
||||||
segments.push(key.into());
|
tokens.push(required_token.clone().into());
|
||||||
tokens.push(key.into());
|
segments.push(required_token.clone());
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let (fwd_token, fwd_pos) = input.peek().expect(NEVER_ENDS);
|
let (fwd_token, fwd_pos) = input.peek().expect(NEVER_ENDS);
|
||||||
settings.pos = *fwd_pos;
|
settings.pos = *fwd_pos;
|
||||||
let settings = settings.level_up();
|
let settings = settings.level_up();
|
||||||
|
|
||||||
let required_token = match parse_func(&segments, fwd_token.syntax().as_ref()) {
|
required_token = match parse_func(&segments, fwd_token.syntax().as_ref()) {
|
||||||
Ok(Some(seg)) => seg,
|
Ok(Some(seg)) => seg,
|
||||||
Ok(None) => break,
|
Ok(None) => break,
|
||||||
Err(err) => return Err(err.0.into_err(settings.pos)),
|
Err(err) => return Err(err.0.into_err(settings.pos)),
|
||||||
@ -2120,11 +2121,23 @@ fn parse_custom_syntax(
|
|||||||
keywords.shrink_to_fit();
|
keywords.shrink_to_fit();
|
||||||
tokens.shrink_to_fit();
|
tokens.shrink_to_fit();
|
||||||
|
|
||||||
|
const KEYWORD_SEMICOLON: &str = Token::SemiColon.literal_syntax();
|
||||||
|
const KEYWORD_CLOSE_BRACE: &str = Token::RightBrace.literal_syntax();
|
||||||
|
|
||||||
|
let self_terminated = match required_token.as_str() {
|
||||||
|
// It is self-terminating if the last symbol is a block
|
||||||
|
CUSTOM_SYNTAX_MARKER_BLOCK => true,
|
||||||
|
// If the last symbol is `;` or `}`, it is self-terminating
|
||||||
|
KEYWORD_SEMICOLON | KEYWORD_CLOSE_BRACE => true,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
Ok(Expr::Custom(
|
Ok(Expr::Custom(
|
||||||
CustomExpr {
|
CustomExpr {
|
||||||
keywords,
|
keywords,
|
||||||
tokens,
|
tokens,
|
||||||
scope_may_be_changed: syntax.scope_may_be_changed,
|
scope_may_be_changed: syntax.scope_may_be_changed,
|
||||||
|
self_terminated,
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
pos,
|
pos,
|
||||||
|
@ -136,6 +136,16 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
|||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Check self-termination
|
||||||
|
engine
|
||||||
|
.register_custom_syntax(&["test1", "$block$"], true, |_, _| Ok(Dynamic::UNIT))?
|
||||||
|
.register_custom_syntax(&["test2", "}"], true, |_, _| Ok(Dynamic::UNIT))?
|
||||||
|
.register_custom_syntax(&["test3", ";"], true, |_, _| Ok(Dynamic::UNIT))?;
|
||||||
|
|
||||||
|
assert_eq!(engine.eval::<INT>("test1 { x = y + z; } 42")?, 42);
|
||||||
|
assert_eq!(engine.eval::<INT>("test2 } 42")?, 42);
|
||||||
|
assert_eq!(engine.eval::<INT>("test3; 42")?, 42);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user