diff --git a/CHANGELOG.md b/CHANGELOG.md index 0615876e..c0a03bf8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Bug fixes * Self-contained `AST` now works properly with `Engine::call_fn`. * 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 ---------------- diff --git a/src/ast/expr.rs b/src/ast/expr.rs index 1ec892a0..263b4eca 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -799,6 +799,8 @@ impl Expr { match token { #[cfg(not(feature = "no_object"))] Token::Period => return true, + #[cfg(not(feature = "no_index"))] + Token::LeftBracket => return true, _ => (), } @@ -823,15 +825,9 @@ impl Expr { | Self::Index(..) | Self::Array(..) | Self::Map(..) - | Self::Custom(..) => match token { - #[cfg(not(feature = "no_index"))] - Token::LeftBracket => true, - _ => false, - }, + | Self::Custom(..) => false, Self::Variable(..) => match token { - #[cfg(not(feature = "no_index"))] - Token::LeftBracket => true, Token::LeftParen => true, Token::Unit => true, Token::Bang => true, @@ -840,8 +836,6 @@ impl Expr { }, Self::Property(..) => match token { - #[cfg(not(feature = "no_index"))] - Token::LeftBracket => true, Token::LeftParen => true, _ => false, }, diff --git a/src/parser.rs b/src/parser.rs index 758cb63a..5ab4b0b1 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -631,6 +631,7 @@ impl Engine { state: &mut ParseState, lib: &mut FnLib, lhs: Expr, + chained: bool, settings: ParseSettings, ) -> ParseResult { #[cfg(not(feature = "unchecked"))] @@ -640,113 +641,100 @@ impl Engine { let idx_expr = self.parse_expr(input, state, lib, settings.level_up())?; - // Check type of indexing - must be integer or string - match idx_expr { - Expr::IntegerConstant(.., pos) => match lhs { - Expr::IntegerConstant(..) - | Expr::Array(..) - | Expr::StringConstant(..) - | Expr::InterpolatedString(..) => (), + // Check types of indexing that cannot be overridden + // - arrays, maps, strings, bit-fields + match lhs { + _ if chained => (), - Expr::Map(..) => { + Expr::Map(..) => match idx_expr { + // lhs[int] + Expr::IntegerConstant(..) => { return Err(PERR::MalformedIndexExpr( - "Object map access 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(), + "Object map expects string index, not a number".into(), ) .into_err(idx_expr.start_position())) } + // lhs[string] + Expr::StringConstant(..) | Expr::InterpolatedString(..) => (), + + // lhs[float] #[cfg(not(feature = "no_float"))] Expr::FloatConstant(..) => { 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())) } - - Expr::CharConstant(..) - | Expr::And(..) - | Expr::Or(..) - | Expr::BoolConstant(..) - | Expr::Unit(..) => { + // lhs[char] + Expr::CharConstant(..) => { 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] - #[cfg(not(feature = "no_float"))] - x @ Expr::FloatConstant(..) => { - return Err(PERR::MalformedIndexExpr( - "Array access expects integer index, not a float".into(), - ) - .into_err(x.start_position())) - } - // lhs[char] - x @ Expr::CharConstant(..) => { - return Err(PERR::MalformedIndexExpr( - "Array access expects integer index, not a character".into(), - ) - .into_err(x.start_position())) - } - // lhs[()] - x @ Expr::Unit(..) => { - return Err(PERR::MalformedIndexExpr( - "Array access expects integer index, not ()".into(), - ) - .into_err(x.start_position())) - } - // lhs[??? && ???], lhs[??? || ???] - x @ Expr::And(..) | x @ Expr::Or(..) => { - return Err(PERR::MalformedIndexExpr( - "Array access expects integer index, not a boolean".into(), - ) - .into_err(x.start_position())) - } - // lhs[true], lhs[false] - x @ Expr::BoolConstant(..) => { - return Err(PERR::MalformedIndexExpr( - "Array access expects integer index, not a boolean".into(), - ) - .into_err(x.start_position())) - } - // All other expressions + Expr::IntegerConstant(..) + | Expr::Array(..) + | Expr::StringConstant(..) + | Expr::InterpolatedString(..) => match idx_expr { + // lhs[int] + Expr::IntegerConstant(..) => (), + + // lhs[string] + Expr::StringConstant(..) | Expr::InterpolatedString(..) => { + return Err(PERR::MalformedIndexExpr( + "Array, string or bit-field expects numeric index, not a string".into(), + ) + .into_err(idx_expr.start_position())) + } + // lhs[float] + #[cfg(not(feature = "no_float"))] + Expr::FloatConstant(..) => { + return Err(PERR::MalformedIndexExpr( + "Array, string or bit-field expects integer index, not a float".into(), + ) + .into_err(idx_expr.start_position())) + } + // lhs[char] + Expr::CharConstant(..) => { + return Err(PERR::MalformedIndexExpr( + "Array, string or bit-field expects integer index, not a character".into(), + ) + .into_err(idx_expr.start_position())) + } + // lhs[()] + Expr::Unit(..) => { + return Err(PERR::MalformedIndexExpr( + "Array, string or bit-field expects integer 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( + "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, lib, idx_expr, + true, settings.level_up(), )?; // Indexing binds to right @@ -1628,7 +1617,7 @@ impl Engine { // Indexing #[cfg(not(feature = "no_index"))] (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 #[cfg(not(feature = "no_object"))]