Catch indexing errors at compile time.

This commit is contained in:
Stephen Chung 2020-03-09 10:41:17 +08:00
parent 01d04f717b
commit e54fb54da2
2 changed files with 86 additions and 9 deletions

View File

@ -64,9 +64,9 @@ pub enum ParseErrorType {
/// An open `[` is missing the corresponding closing `]`.
MissingRightBracket(String),
/// An expression in function call arguments `()` has syntax error.
MalformedCallExpr,
MalformedCallExpr(String),
/// An expression in indexing brackets `[]` has syntax error.
MalformedIndexExpr,
MalformedIndexExpr(String),
/// Missing a variable name after the `let` keyword.
VarExpectsIdentifier,
/// Defining a function `fn` in an appropriate place (e.g. inside another function).
@ -110,8 +110,8 @@ impl Error for ParseError {
ParseErrorType::MissingLeftBrace => "Expecting '{'",
ParseErrorType::MissingRightBrace(_) => "Expecting '}'",
ParseErrorType::MissingRightBracket(_) => "Expecting ']'",
ParseErrorType::MalformedCallExpr => "Invalid expression in function call arguments",
ParseErrorType::MalformedIndexExpr => "Invalid index in indexing expression",
ParseErrorType::MalformedCallExpr(_) => "Invalid expression in function call arguments",
ParseErrorType::MalformedIndexExpr(_) => "Invalid index in indexing expression",
ParseErrorType::VarExpectsIdentifier => "Expecting name of a variable",
ParseErrorType::FnMissingName => "Expecting name in function declaration",
ParseErrorType::FnMissingParams(_) => "Expecting parameters in function declaration",
@ -128,7 +128,11 @@ impl Error for ParseError {
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
ParseErrorType::BadInput(ref s) => write!(f, "{}", s)?,
ParseErrorType::BadInput(ref s)
| ParseErrorType::MalformedIndexExpr(ref s)
| ParseErrorType::MalformedCallExpr(ref s) => {
write!(f, "{}", if s.is_empty() { self.description() } else { s })?
}
ParseErrorType::UnknownOperator(ref s) => write!(f, "{}: '{}'", self.description(), s)?,
ParseErrorType::FnMissingParams(ref s) => {
write!(f, "Expecting parameters for function '{}'", s)?

View File

@ -1146,7 +1146,66 @@ fn parse_index_expr<'a>(
input: &mut Peekable<TokenIterator<'a>>,
pos: Position,
) -> Result<Expr, ParseError> {
parse_expr(input).and_then(|idx_expr| match input.peek() {
let idx_expr = parse_expr(input)?;
// Check type of indexing - must be integer
match &idx_expr {
Expr::IntegerConstant(i, pos) if *i < 0 => {
return Err(ParseError::new(
PERR::MalformedIndexExpr(format!(
"Array access expects non-negative index: {} < 0",
i
)),
*pos,
))
}
Expr::FloatConstant(_, pos) => {
return Err(ParseError::new(
PERR::MalformedIndexExpr("Array access expects integer index, not a float".into()),
*pos,
))
}
Expr::CharConstant(_, pos) => {
return Err(ParseError::new(
PERR::MalformedIndexExpr(
"Array access expects integer index, not a character".into(),
),
*pos,
))
}
Expr::StringConstant(_, pos) => {
return Err(ParseError::new(
PERR::MalformedIndexExpr("Array access expects integer index, not a string".into()),
*pos,
))
}
Expr::Assignment(_, _, pos) | Expr::Unit(pos) => {
return Err(ParseError::new(
PERR::MalformedIndexExpr("Array access expects integer index, not ()".into()),
*pos,
))
}
Expr::And(lhs, _) | Expr::Or(lhs, _) => {
return Err(ParseError::new(
PERR::MalformedIndexExpr(
"Array access expects integer index, not a boolean".into(),
),
lhs.position(),
))
}
Expr::True(pos) | Expr::False(pos) => {
return Err(ParseError::new(
PERR::MalformedIndexExpr(
"Array access expects integer index, not a boolean".into(),
),
*pos,
))
}
_ => (),
}
// Check if there is a closing bracket
match input.peek() {
Some(&(Token::RightBracket, _)) => {
input.next();
return Ok(Expr::Index(lhs, Box::new(idx_expr), pos));
@ -1163,7 +1222,7 @@ fn parse_index_expr<'a>(
Position::eof(),
))
}
})
}
}
fn parse_ident_expr<'a>(
@ -1728,8 +1787,22 @@ fn parse_fn<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<FnDef<'static
Some((Token::Identifier(s), _)) => {
params.push(s.into());
}
Some((_, pos)) => return Err(ParseError::new(PERR::MalformedCallExpr, pos)),
None => return Err(ParseError::new(PERR::MalformedCallExpr, Position::eof())),
Some((_, pos)) => {
return Err(ParseError::new(
PERR::MalformedCallExpr(
"Function call arguments missing either a ',' or a ')'".into(),
),
pos,
))
}
None => {
return Err(ParseError::new(
PERR::MalformedCallExpr(
"Function call arguments missing a closing ')'".into(),
),
Position::eof(),
))
}
}
},
}