Add $$ marker to custom syntax.
This commit is contained in:
parent
76ac24dd85
commit
79dd3f8186
@ -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
|
||||||
------------
|
------------
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
||||||
|
29
src/parse.rs
29
src/parse.rs
@ -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,
|
||||||
|
@ -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")?,
|
||||||
|
Loading…
Reference in New Issue
Block a user