Fix indexing parsing.

This commit is contained in:
Stephen Chung 2022-05-17 11:06:34 +08:00
parent c7aea45d4b
commit 04df4d2547
3 changed files with 84 additions and 100 deletions

View File

@ -9,6 +9,7 @@ Bug fixes
* Self-contained `AST` now works properly with `Engine::call_fn`. * Self-contained `AST` now works properly with `Engine::call_fn`.
* Missing `to_int` from `Decimal` is added. * Missing `to_int` from `Decimal` is added.
* Parsing of index expressions is relaxed and many cases no longer result in an index-type error to allow for custom indexers.
Deprecated API's Deprecated API's
---------------- ----------------

View File

@ -799,6 +799,8 @@ impl Expr {
match token { match token {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Token::Period => return true, Token::Period => return true,
#[cfg(not(feature = "no_index"))]
Token::LeftBracket => return true,
_ => (), _ => (),
} }
@ -823,15 +825,9 @@ impl Expr {
| Self::Index(..) | Self::Index(..)
| Self::Array(..) | Self::Array(..)
| Self::Map(..) | Self::Map(..)
| Self::Custom(..) => match token { | Self::Custom(..) => false,
#[cfg(not(feature = "no_index"))]
Token::LeftBracket => true,
_ => false,
},
Self::Variable(..) => match token { Self::Variable(..) => match token {
#[cfg(not(feature = "no_index"))]
Token::LeftBracket => true,
Token::LeftParen => true, Token::LeftParen => true,
Token::Unit => true, Token::Unit => true,
Token::Bang => true, Token::Bang => true,
@ -840,8 +836,6 @@ impl Expr {
}, },
Self::Property(..) => match token { Self::Property(..) => match token {
#[cfg(not(feature = "no_index"))]
Token::LeftBracket => true,
Token::LeftParen => true, Token::LeftParen => true,
_ => false, _ => false,
}, },

View File

@ -631,6 +631,7 @@ impl Engine {
state: &mut ParseState, state: &mut ParseState,
lib: &mut FnLib, lib: &mut FnLib,
lhs: Expr, lhs: Expr,
chained: bool,
settings: ParseSettings, settings: ParseSettings,
) -> ParseResult<Expr> { ) -> ParseResult<Expr> {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
@ -640,113 +641,100 @@ impl Engine {
let idx_expr = self.parse_expr(input, state, lib, settings.level_up())?; let idx_expr = self.parse_expr(input, state, lib, settings.level_up())?;
// Check type of indexing - must be integer or string // Check types of indexing that cannot be overridden
match idx_expr { // - arrays, maps, strings, bit-fields
Expr::IntegerConstant(.., pos) => match lhs { match lhs {
Expr::IntegerConstant(..) _ if chained => (),
| Expr::Array(..)
| Expr::StringConstant(..)
| Expr::InterpolatedString(..) => (),
Expr::Map(..) => { Expr::Map(..) => match idx_expr {
// lhs[int]
Expr::IntegerConstant(..) => {
return Err(PERR::MalformedIndexExpr( return Err(PERR::MalformedIndexExpr(
"Object map access expects string index, not a number".into(), "Object map expects string index, not a number".into(),
)
.into_err(pos))
}
#[cfg(not(feature = "no_float"))]
Expr::FloatConstant(..) => {
return Err(PERR::MalformedIndexExpr(
"Only arrays, object maps and strings can be indexed".into(),
)
.into_err(lhs.start_position()))
}
Expr::CharConstant(..)
| Expr::And(..)
| Expr::Or(..)
| Expr::BoolConstant(..)
| Expr::Unit(..) => {
return Err(PERR::MalformedIndexExpr(
"Only arrays, object maps and strings can be indexed".into(),
)
.into_err(lhs.start_position()))
}
_ => (),
},
// lhs[string]
Expr::StringConstant(..) | Expr::InterpolatedString(..) => match lhs {
Expr::Map(..) => (),
Expr::Array(..) | Expr::StringConstant(..) | Expr::InterpolatedString(..) => {
return Err(PERR::MalformedIndexExpr(
"Array or string expects numeric index, not a string".into(),
) )
.into_err(idx_expr.start_position())) .into_err(idx_expr.start_position()))
} }
// lhs[string]
Expr::StringConstant(..) | Expr::InterpolatedString(..) => (),
// lhs[float]
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Expr::FloatConstant(..) => { Expr::FloatConstant(..) => {
return Err(PERR::MalformedIndexExpr( return Err(PERR::MalformedIndexExpr(
"Only arrays, object maps and strings can be indexed".into(), "Object map expects string index, not a float".into(),
) )
.into_err(lhs.start_position())) .into_err(idx_expr.start_position()))
} }
// lhs[char]
Expr::CharConstant(..) Expr::CharConstant(..) => {
| Expr::And(..)
| Expr::Or(..)
| Expr::BoolConstant(..)
| Expr::Unit(..) => {
return Err(PERR::MalformedIndexExpr( return Err(PERR::MalformedIndexExpr(
"Only arrays, object maps and strings can be indexed".into(), "Object map expects string index, not a character".into(),
) )
.into_err(lhs.start_position())) .into_err(idx_expr.start_position()))
}
// lhs[()]
Expr::Unit(..) => {
return Err(PERR::MalformedIndexExpr(
"Object map expects string index, not ()".into(),
)
.into_err(idx_expr.start_position()))
}
// lhs[??? && ???], lhs[??? || ???], lhs[true], lhs[false]
Expr::And(..) | Expr::Or(..) | Expr::BoolConstant(..) => {
return Err(PERR::MalformedIndexExpr(
"Object map expects string index, not a boolean".into(),
)
.into_err(idx_expr.start_position()))
} }
_ => (), _ => (),
}, },
// lhs[float] Expr::IntegerConstant(..)
#[cfg(not(feature = "no_float"))] | Expr::Array(..)
x @ Expr::FloatConstant(..) => { | Expr::StringConstant(..)
return Err(PERR::MalformedIndexExpr( | Expr::InterpolatedString(..) => match idx_expr {
"Array access expects integer index, not a float".into(), // lhs[int]
) Expr::IntegerConstant(..) => (),
.into_err(x.start_position()))
} // lhs[string]
// lhs[char] Expr::StringConstant(..) | Expr::InterpolatedString(..) => {
x @ Expr::CharConstant(..) => { return Err(PERR::MalformedIndexExpr(
return Err(PERR::MalformedIndexExpr( "Array, string or bit-field expects numeric index, not a string".into(),
"Array access expects integer index, not a character".into(), )
) .into_err(idx_expr.start_position()))
.into_err(x.start_position())) }
} // lhs[float]
// lhs[()] #[cfg(not(feature = "no_float"))]
x @ Expr::Unit(..) => { Expr::FloatConstant(..) => {
return Err(PERR::MalformedIndexExpr( return Err(PERR::MalformedIndexExpr(
"Array access expects integer index, not ()".into(), "Array, string or bit-field expects integer index, not a float".into(),
) )
.into_err(x.start_position())) .into_err(idx_expr.start_position()))
} }
// lhs[??? && ???], lhs[??? || ???] // lhs[char]
x @ Expr::And(..) | x @ Expr::Or(..) => { Expr::CharConstant(..) => {
return Err(PERR::MalformedIndexExpr( return Err(PERR::MalformedIndexExpr(
"Array access expects integer index, not a boolean".into(), "Array, string or bit-field expects integer index, not a character".into(),
) )
.into_err(x.start_position())) .into_err(idx_expr.start_position()))
} }
// lhs[true], lhs[false] // lhs[()]
x @ Expr::BoolConstant(..) => { Expr::Unit(..) => {
return Err(PERR::MalformedIndexExpr( return Err(PERR::MalformedIndexExpr(
"Array access expects integer index, not a boolean".into(), "Array, string or bit-field expects integer index, not ()".into(),
) )
.into_err(x.start_position())) .into_err(idx_expr.start_position()))
} }
// All other expressions // lhs[??? && ???], lhs[??? || ???], lhs[true], lhs[false]
Expr::And(..) | Expr::Or(..) | Expr::BoolConstant(..) => {
return Err(PERR::MalformedIndexExpr(
"Array, string or bit-field expects integer index, not a boolean".into(),
)
.into_err(idx_expr.start_position()))
}
_ => (),
},
_ => (), _ => (),
} }
@ -767,6 +755,7 @@ impl Engine {
state, state,
lib, lib,
idx_expr, idx_expr,
true,
settings.level_up(), settings.level_up(),
)?; )?;
// Indexing binds to right // Indexing binds to right
@ -1628,7 +1617,7 @@ impl Engine {
// Indexing // Indexing
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
(expr, Token::LeftBracket) => { (expr, Token::LeftBracket) => {
self.parse_index_chain(input, state, lib, expr, settings.level_up())? self.parse_index_chain(input, state, lib, expr, false, settings.level_up())?
} }
// Property access // Property access
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]