diff --git a/CHANGELOG.md b/CHANGELOG.md index 097b2728..58bd82da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ New features ------------ * `#[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 ------------ diff --git a/src/ast.rs b/src/ast.rs index 8325ba51..f5f72c5a 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1564,7 +1564,7 @@ impl Stmt { #[derive(Debug, Clone, Hash)] pub struct CustomExpr { /// List of keywords. - pub keywords: StaticVec, + pub inputs: StaticVec, /// Is the current [`Scope`][crate::Scope] possibly modified by this custom statement /// (e.g. introducing a new variable)? pub scope_may_be_changed: bool, @@ -2366,7 +2366,7 @@ impl Expr { } } Self::Custom(x, _) => { - for e in &x.keywords { + for e in &x.inputs { if !e.walk(path, on_node) { return false; } diff --git a/src/custom_syntax.rs b/src/custom_syntax.rs index 7f98ae18..9de6af3f 100644 --- a/src/custom_syntax.rs +++ b/src/custom_syntax.rs @@ -33,6 +33,8 @@ pub mod markers { pub const CUSTOM_SYNTAX_MARKER_FLOAT: &str = "$float$"; /// Special marker for matching a boolean value. 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. diff --git a/src/engine.rs b/src/engine.rs index b5349485..134c6669 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -2271,7 +2271,7 @@ impl Engine { Expr::Unit(_) => Ok(Dynamic::UNIT), 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 .tokens .first() diff --git a/src/optimize.rs b/src/optimize.rs index 8479ecd4..7d529293 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -1079,7 +1079,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) { if x.scope_may_be_changed { 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 diff --git a/src/parse.rs b/src/parse.rs index e33d5c59..296566fd 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -1978,7 +1978,7 @@ fn parse_custom_syntax( pos: Position, ) -> Result { let mut settings = settings; - let mut keywords = StaticVec::::new(); + let mut inputs = StaticVec::::new(); let mut segments = StaticVec::new(); let mut tokens = StaticVec::new(); @@ -2002,6 +2002,13 @@ fn parse_custom_syntax( let settings = settings.level_up(); 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(None) => break, Err(err) => return Err(err.0.into_err(settings.pos)), @@ -2013,24 +2020,24 @@ fn parse_custom_syntax( let name = state.get_identifier(name); segments.push(name.clone().into()); 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 => { let (symbol, pos) = parse_symbol(input)?; let symbol: ImmutableString = state.get_identifier(symbol).into(); segments.push(symbol.clone()); 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 => { - 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); segments.push(keyword.clone().into()); tokens.push(keyword); } CUSTOM_SYNTAX_MARKER_BLOCK => match parse_block(input, state, lib, settings)? { 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); segments.push(keyword.clone().into()); tokens.push(keyword); @@ -2039,7 +2046,7 @@ fn parse_custom_syntax( }, CUSTOM_SYNTAX_MARKER_BOOL => match input.next().expect(NEVER_ENDS) { (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()); 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) { (Token::IntegerConstant(i), pos) => { - keywords.push(Expr::IntegerConstant(i, pos)); + inputs.push(Expr::IntegerConstant(i, pos)); segments.push(i.to_string().into()); tokens.push(state.get_identifier(CUSTOM_SYNTAX_MARKER_INT)); } @@ -2066,7 +2073,7 @@ fn parse_custom_syntax( #[cfg(not(feature = "no_float"))] CUSTOM_SYNTAX_MARKER_FLOAT => match input.next().expect(NEVER_ENDS) { (Token::FloatConstant(f), pos) => { - keywords.push(Expr::FloatConstant(f, pos)); + inputs.push(Expr::FloatConstant(f, pos)); segments.push(f.to_string().into()); 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) { (Token::StringConstant(s), pos) => { 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); 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(); const KEYWORD_SEMICOLON: &str = Token::SemiColon.literal_syntax(); @@ -2121,7 +2128,7 @@ fn parse_custom_syntax( Ok(Expr::Custom( CustomExpr { - keywords, + inputs, tokens, scope_may_be_changed: syntax.scope_may_be_changed, self_terminated, diff --git a/tests/custom_syntax.rs b/tests/custom_syntax.rs index f1b5a297..d9d6171f 100644 --- a/tests/custom_syntax.rs +++ b/tests/custom_syntax.rs @@ -194,7 +194,8 @@ fn test_custom_syntax_raw() -> Result<(), Box> { 0 => unreachable!(), 1 => Ok(Some("$ident$".into())), 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()) .into_err(Position::NONE) .into()), @@ -206,7 +207,17 @@ fn test_custom_syntax_raw() -> Result<(), Box> { context.scope_mut().push("foo", 999 as INT); Ok(match inputs[0].get_variable_name().unwrap() { + "world" + if inputs + .last() + .unwrap() + .get_literal_value::() + .map_or(false, |s| s == "$$hello") => + { + 0 as INT + } "world" => 123 as INT, + "kitty" if inputs.len() > 1 => 999 as INT, "kitty" => 42 as INT, _ => unreachable!(), } @@ -214,7 +225,7 @@ fn test_custom_syntax_raw() -> Result<(), Box> { }, ); - assert_eq!(engine.eval::("hello world")?, 123); + assert_eq!(engine.eval::("hello world")?, 0); assert_eq!(engine.eval::("hello kitty")?, 42); assert_eq!( engine.eval::("let foo = 0; (hello kitty) + foo")?,