Add $$ marker to custom syntax.

This commit is contained in:
Stephen Chung 2021-10-25 22:41:42 +08:00
parent 76ac24dd85
commit 79dd3f8186
7 changed files with 38 additions and 17 deletions

View File

@ -8,6 +8,7 @@ New features
------------ ------------
* `#[cfg(...)]` attributes can now be put directly on plugin functions or function defined in a plugin module. * `#[cfg(...)]` attributes can now be put directly on plugin functions or function defined in a plugin module.
* A custom syntax parser can now return a symbol starting with `$$` to inform the implementation function which syntax variant was actually parsed.
Enhancements Enhancements
------------ ------------

View File

@ -1564,7 +1564,7 @@ impl Stmt {
#[derive(Debug, Clone, Hash)] #[derive(Debug, Clone, Hash)]
pub struct CustomExpr { pub struct CustomExpr {
/// List of keywords. /// List of keywords.
pub keywords: StaticVec<Expr>, pub inputs: StaticVec<Expr>,
/// Is the current [`Scope`][crate::Scope] possibly modified by this custom statement /// Is the current [`Scope`][crate::Scope] possibly modified by this custom statement
/// (e.g. introducing a new variable)? /// (e.g. introducing a new variable)?
pub scope_may_be_changed: bool, pub scope_may_be_changed: bool,
@ -2366,7 +2366,7 @@ impl Expr {
} }
} }
Self::Custom(x, _) => { Self::Custom(x, _) => {
for e in &x.keywords { for e in &x.inputs {
if !e.walk(path, on_node) { if !e.walk(path, on_node) {
return false; return false;
} }

View File

@ -33,6 +33,8 @@ pub mod markers {
pub const CUSTOM_SYNTAX_MARKER_FLOAT: &str = "$float$"; pub const CUSTOM_SYNTAX_MARKER_FLOAT: &str = "$float$";
/// Special marker for matching a boolean value. /// Special marker for matching a boolean value.
pub const CUSTOM_SYNTAX_MARKER_BOOL: &str = "$bool$"; pub const CUSTOM_SYNTAX_MARKER_BOOL: &str = "$bool$";
/// Special marker for identifying the custom syntax variant.
pub const CUSTOM_SYNTAX_MARKER_SYNTAX_VARIANT: &str = "$$";
} }
/// A general expression evaluation trait object. /// A general expression evaluation trait object.

View File

@ -2271,7 +2271,7 @@ impl Engine {
Expr::Unit(_) => Ok(Dynamic::UNIT), Expr::Unit(_) => Ok(Dynamic::UNIT),
Expr::Custom(custom, _) => { Expr::Custom(custom, _) => {
let expressions: StaticVec<_> = custom.keywords.iter().map(Into::into).collect(); let expressions: StaticVec<_> = custom.inputs.iter().map(Into::into).collect();
let key_token = custom let key_token = custom
.tokens .tokens
.first() .first()

View File

@ -1079,7 +1079,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
if x.scope_may_be_changed { if x.scope_may_be_changed {
state.propagate_constants = false; state.propagate_constants = false;
} }
x.keywords.iter_mut().for_each(|expr| optimize_expr(expr, state, false)); x.inputs.iter_mut().for_each(|expr| optimize_expr(expr, state, false));
} }
// All other expressions - skip // All other expressions - skip

View File

@ -1978,7 +1978,7 @@ fn parse_custom_syntax(
pos: Position, pos: Position,
) -> Result<Expr, ParseError> { ) -> Result<Expr, ParseError> {
let mut settings = settings; let mut settings = settings;
let mut keywords = StaticVec::<Expr>::new(); let mut inputs = StaticVec::<Expr>::new();
let mut segments = StaticVec::new(); let mut segments = StaticVec::new();
let mut tokens = StaticVec::new(); let mut tokens = StaticVec::new();
@ -2002,6 +2002,13 @@ fn parse_custom_syntax(
let settings = settings.level_up(); let settings = settings.level_up();
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))
if seg.starts_with(CUSTOM_SYNTAX_MARKER_SYNTAX_VARIANT)
&& seg.len() > CUSTOM_SYNTAX_MARKER_SYNTAX_VARIANT.len() =>
{
inputs.push(Expr::StringConstant(state.get_identifier(seg).into(), pos));
break;
}
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)),
@ -2013,24 +2020,24 @@ fn parse_custom_syntax(
let name = state.get_identifier(name); let name = state.get_identifier(name);
segments.push(name.clone().into()); segments.push(name.clone().into());
tokens.push(state.get_identifier(CUSTOM_SYNTAX_MARKER_IDENT)); tokens.push(state.get_identifier(CUSTOM_SYNTAX_MARKER_IDENT));
keywords.push(Expr::Variable(None, pos, (None, None, name).into())); inputs.push(Expr::Variable(None, pos, (None, None, name).into()));
} }
CUSTOM_SYNTAX_MARKER_SYMBOL => { CUSTOM_SYNTAX_MARKER_SYMBOL => {
let (symbol, pos) = parse_symbol(input)?; let (symbol, pos) = parse_symbol(input)?;
let symbol: ImmutableString = state.get_identifier(symbol).into(); let symbol: ImmutableString = state.get_identifier(symbol).into();
segments.push(symbol.clone()); segments.push(symbol.clone());
tokens.push(state.get_identifier(CUSTOM_SYNTAX_MARKER_SYMBOL)); tokens.push(state.get_identifier(CUSTOM_SYNTAX_MARKER_SYMBOL));
keywords.push(Expr::StringConstant(symbol, pos)); inputs.push(Expr::StringConstant(symbol, pos));
} }
CUSTOM_SYNTAX_MARKER_EXPR => { CUSTOM_SYNTAX_MARKER_EXPR => {
keywords.push(parse_expr(input, state, lib, settings)?); inputs.push(parse_expr(input, state, lib, settings)?);
let keyword = state.get_identifier(CUSTOM_SYNTAX_MARKER_EXPR); let keyword = state.get_identifier(CUSTOM_SYNTAX_MARKER_EXPR);
segments.push(keyword.clone().into()); segments.push(keyword.clone().into());
tokens.push(keyword); tokens.push(keyword);
} }
CUSTOM_SYNTAX_MARKER_BLOCK => match parse_block(input, state, lib, settings)? { CUSTOM_SYNTAX_MARKER_BLOCK => match parse_block(input, state, lib, settings)? {
block @ Stmt::Block(_, _) => { block @ Stmt::Block(_, _) => {
keywords.push(Expr::Stmt(Box::new(block.into()))); inputs.push(Expr::Stmt(Box::new(block.into())));
let keyword = state.get_identifier(CUSTOM_SYNTAX_MARKER_BLOCK); let keyword = state.get_identifier(CUSTOM_SYNTAX_MARKER_BLOCK);
segments.push(keyword.clone().into()); segments.push(keyword.clone().into());
tokens.push(keyword); tokens.push(keyword);
@ -2039,7 +2046,7 @@ fn parse_custom_syntax(
}, },
CUSTOM_SYNTAX_MARKER_BOOL => match input.next().expect(NEVER_ENDS) { CUSTOM_SYNTAX_MARKER_BOOL => match input.next().expect(NEVER_ENDS) {
(b @ Token::True, pos) | (b @ Token::False, pos) => { (b @ Token::True, pos) | (b @ Token::False, pos) => {
keywords.push(Expr::BoolConstant(b == Token::True, pos)); inputs.push(Expr::BoolConstant(b == Token::True, pos));
segments.push(state.get_identifier(b.literal_syntax()).into()); segments.push(state.get_identifier(b.literal_syntax()).into());
tokens.push(state.get_identifier(CUSTOM_SYNTAX_MARKER_BOOL)); tokens.push(state.get_identifier(CUSTOM_SYNTAX_MARKER_BOOL));
} }
@ -2052,7 +2059,7 @@ fn parse_custom_syntax(
}, },
CUSTOM_SYNTAX_MARKER_INT => match input.next().expect(NEVER_ENDS) { CUSTOM_SYNTAX_MARKER_INT => match input.next().expect(NEVER_ENDS) {
(Token::IntegerConstant(i), pos) => { (Token::IntegerConstant(i), pos) => {
keywords.push(Expr::IntegerConstant(i, pos)); inputs.push(Expr::IntegerConstant(i, pos));
segments.push(i.to_string().into()); segments.push(i.to_string().into());
tokens.push(state.get_identifier(CUSTOM_SYNTAX_MARKER_INT)); tokens.push(state.get_identifier(CUSTOM_SYNTAX_MARKER_INT));
} }
@ -2066,7 +2073,7 @@ fn parse_custom_syntax(
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
CUSTOM_SYNTAX_MARKER_FLOAT => match input.next().expect(NEVER_ENDS) { CUSTOM_SYNTAX_MARKER_FLOAT => match input.next().expect(NEVER_ENDS) {
(Token::FloatConstant(f), pos) => { (Token::FloatConstant(f), pos) => {
keywords.push(Expr::FloatConstant(f, pos)); inputs.push(Expr::FloatConstant(f, pos));
segments.push(f.to_string().into()); segments.push(f.to_string().into());
tokens.push(state.get_identifier(CUSTOM_SYNTAX_MARKER_FLOAT)); tokens.push(state.get_identifier(CUSTOM_SYNTAX_MARKER_FLOAT));
} }
@ -2080,7 +2087,7 @@ fn parse_custom_syntax(
CUSTOM_SYNTAX_MARKER_STRING => match input.next().expect(NEVER_ENDS) { CUSTOM_SYNTAX_MARKER_STRING => match input.next().expect(NEVER_ENDS) {
(Token::StringConstant(s), pos) => { (Token::StringConstant(s), pos) => {
let s: ImmutableString = state.get_identifier(s).into(); let s: ImmutableString = state.get_identifier(s).into();
keywords.push(Expr::StringConstant(s.clone(), pos)); inputs.push(Expr::StringConstant(s.clone(), pos));
segments.push(s); segments.push(s);
tokens.push(state.get_identifier(CUSTOM_SYNTAX_MARKER_STRING)); tokens.push(state.get_identifier(CUSTOM_SYNTAX_MARKER_STRING));
} }
@ -2105,7 +2112,7 @@ fn parse_custom_syntax(
} }
} }
keywords.shrink_to_fit(); inputs.shrink_to_fit();
tokens.shrink_to_fit(); tokens.shrink_to_fit();
const KEYWORD_SEMICOLON: &str = Token::SemiColon.literal_syntax(); const KEYWORD_SEMICOLON: &str = Token::SemiColon.literal_syntax();
@ -2121,7 +2128,7 @@ fn parse_custom_syntax(
Ok(Expr::Custom( Ok(Expr::Custom(
CustomExpr { CustomExpr {
keywords, inputs,
tokens, tokens,
scope_may_be_changed: syntax.scope_may_be_changed, scope_may_be_changed: syntax.scope_may_be_changed,
self_terminated, self_terminated,

View File

@ -194,7 +194,8 @@ fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
0 => unreachable!(), 0 => unreachable!(),
1 => Ok(Some("$ident$".into())), 1 => Ok(Some("$ident$".into())),
2 => match stream[1].as_str() { 2 => match stream[1].as_str() {
"world" | "kitty" => Ok(None), "world" => Ok(Some("$$hello".into())),
"kitty" => Ok(None),
s => Err(LexError::ImproperSymbol(s.to_string(), Default::default()) s => Err(LexError::ImproperSymbol(s.to_string(), Default::default())
.into_err(Position::NONE) .into_err(Position::NONE)
.into()), .into()),
@ -206,7 +207,17 @@ fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
context.scope_mut().push("foo", 999 as INT); context.scope_mut().push("foo", 999 as INT);
Ok(match inputs[0].get_variable_name().unwrap() { Ok(match inputs[0].get_variable_name().unwrap() {
"world"
if inputs
.last()
.unwrap()
.get_literal_value::<ImmutableString>()
.map_or(false, |s| s == "$$hello") =>
{
0 as INT
}
"world" => 123 as INT, "world" => 123 as INT,
"kitty" if inputs.len() > 1 => 999 as INT,
"kitty" => 42 as INT, "kitty" => 42 as INT,
_ => unreachable!(), _ => unreachable!(),
} }
@ -214,7 +225,7 @@ fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
}, },
); );
assert_eq!(engine.eval::<INT>("hello world")?, 123); assert_eq!(engine.eval::<INT>("hello world")?, 0);
assert_eq!(engine.eval::<INT>("hello kitty")?, 42); assert_eq!(engine.eval::<INT>("hello kitty")?, 42);
assert_eq!( assert_eq!(
engine.eval::<INT>("let foo = 0; (hello kitty) + foo")?, engine.eval::<INT>("let foo = 0; (hello kitty) + foo")?,