Merge branch 'v1.3-fixes'

This commit is contained in:
Stephen Chung 2021-12-16 22:40:25 +08:00
commit b1b2c62d7d
4 changed files with 37 additions and 27 deletions

View File

@ -24,7 +24,7 @@ Version 1.3.1
Bug fixes Bug fixes
--------- ---------
* Custom syntax now works properly inside binary expressions. * Custom syntax now works properly inside binary expressions and with method calls.
Enhancements Enhancements
------------ ------------

View File

@ -2475,7 +2475,8 @@ impl Expr {
| Self::Dot(_, _, _) | Self::Dot(_, _, _)
| Self::Index(_, _, _) | Self::Index(_, _, _)
| Self::Array(_, _) | Self::Array(_, _)
| Self::Map(_, _) => match token { | Self::Map(_, _)
| Self::Custom(_, _) => match token {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Token::LeftBracket => true, Token::LeftBracket => true,
_ => false, _ => false,
@ -2497,8 +2498,6 @@ impl Expr {
_ => false, _ => false,
}, },
Self::Custom(_, _) => false,
Self::Stack(_, _) => false, Self::Stack(_, _) => false,
} }
} }

View File

@ -1155,7 +1155,7 @@ fn parse_primary(
let mut settings = settings; let mut settings = settings;
settings.pos = *token_pos; settings.pos = *token_pos;
let mut root_expr = match token { let root_expr = match token {
Token::EOF => return Err(PERR::UnexpectedEOF.into_err(settings.pos)), Token::EOF => return Err(PERR::UnexpectedEOF.into_err(settings.pos)),
Token::IntegerConstant(_) Token::IntegerConstant(_)
@ -1314,6 +1314,20 @@ fn parse_primary(
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Token::MapStart => parse_map_literal(input, state, lib, settings.level_up())?, Token::MapStart => parse_map_literal(input, state, lib, settings.level_up())?,
// Custom syntax.
Token::Custom(key) | Token::Reserved(key) | Token::Identifier(key)
if state.engine.custom_syntax.contains_key(&**key) =>
{
let (key, syntax) = state
.engine
.custom_syntax
.get_key_value(&**key)
.expect("exists");
let (_, pos) = input.next().expect(NEVER_ENDS);
let settings2 = settings.level_up();
parse_custom_syntax(input, state, lib, settings2, key, syntax, pos)?
}
// Identifier // Identifier
Token::Identifier(_) => { Token::Identifier(_) => {
let s = match input.next().expect(NEVER_ENDS) { let s = match input.next().expect(NEVER_ENDS) {
@ -1415,18 +1429,31 @@ fn parse_primary(
} }
}; };
parse_postfix(input, state, lib, root_expr, settings)
}
/// Tail processing of all possible postfix operators of a primary expression.
fn parse_postfix(
input: &mut TokenStream,
state: &mut ParseState,
lib: &mut FunctionsLib,
mut lhs: Expr,
settings: ParseSettings,
) -> Result<Expr, ParseError> {
let mut settings = settings;
// Tail processing all possible postfix operators // Tail processing all possible postfix operators
loop { loop {
let (tail_token, _) = input.peek().expect(NEVER_ENDS); let (tail_token, _) = input.peek().expect(NEVER_ENDS);
if !root_expr.is_valid_postfix(tail_token) { if !lhs.is_valid_postfix(tail_token) {
break; break;
} }
let (tail_token, tail_pos) = input.next().expect(NEVER_ENDS); let (tail_token, tail_pos) = input.next().expect(NEVER_ENDS);
settings.pos = tail_pos; settings.pos = tail_pos;
root_expr = match (root_expr, tail_token) { lhs = match (lhs, tail_token) {
// Qualified function call with ! // Qualified function call with !
(Expr::Variable(_, _, x), Token::Bang) if x.1.is_some() => { (Expr::Variable(_, _, x), Token::Bang) if x.1.is_some() => {
return if !match_token(input, Token::LeftParen).0 { return if !match_token(input, Token::LeftParen).0 {
@ -1520,7 +1547,7 @@ fn parse_primary(
} }
// Cache the hash key for namespace-qualified variables // Cache the hash key for namespace-qualified variables
let namespaced_variable = match root_expr { let namespaced_variable = match lhs {
Expr::Variable(_, _, ref mut x) if x.1.is_some() => Some(x.as_mut()), Expr::Variable(_, _, ref mut x) if x.1.is_some() => Some(x.as_mut()),
Expr::Index(ref mut x, _, _) | Expr::Dot(ref mut x, _, _) => match x.lhs { Expr::Index(ref mut x, _, _) | Expr::Dot(ref mut x, _, _) => match x.lhs {
Expr::Variable(_, _, ref mut x) if x.1.is_some() => Some(x.as_mut()), Expr::Variable(_, _, ref mut x) if x.1.is_some() => Some(x.as_mut()),
@ -1553,7 +1580,7 @@ fn parse_primary(
} }
// Make sure identifiers are valid // Make sure identifiers are valid
Ok(root_expr) Ok(lhs)
} }
/// Parse a potential unary operator. /// Parse a potential unary operator.
@ -1567,26 +1594,9 @@ fn parse_unary(
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let (token, token_pos) = input.peek().expect(NEVER_ENDS); let (token, token_pos) = input.peek().expect(NEVER_ENDS);
let token_pos = *token_pos;
let mut settings = settings; let mut settings = settings;
settings.pos = token_pos; settings.pos = *token_pos;
// Check if it is a custom syntax.
if !state.engine.custom_syntax.is_empty() {
match token {
Token::Custom(key) | Token::Reserved(key) | Token::Identifier(key) => {
if let Some((key, syntax)) = state.engine.custom_syntax.get_key_value(key.as_ref())
{
input.next().expect(NEVER_ENDS);
return parse_custom_syntax(
input, state, lib, settings, key, syntax, token_pos,
);
}
}
_ => (),
}
}
match token { match token {
// -expr // -expr

View File

@ -267,6 +267,7 @@ fn test_custom_syntax_raw2() -> Result<(), Box<EvalAltResult>> {
assert_eq!(engine.eval::<INT>("#-1")?, -1); assert_eq!(engine.eval::<INT>("#-1")?, -1);
assert_eq!(engine.eval::<INT>("let x = 41; x + #1")?, 42); assert_eq!(engine.eval::<INT>("let x = 41; x + #1")?, 42);
assert_eq!(engine.eval::<INT>("#-42.abs()")?, 42);
assert_eq!(engine.eval::<INT>("#42/2")?, 21); assert_eq!(engine.eval::<INT>("#42/2")?, 21);
assert_eq!(engine.eval::<INT>("sign(#1)")?, 1); assert_eq!(engine.eval::<INT>("sign(#1)")?, 1);