Catch illegal variable names.

This commit is contained in:
Stephen Chung 2020-03-16 12:38:01 +08:00
parent 372321dfe3
commit 2c90fea764
2 changed files with 56 additions and 19 deletions

View File

@ -18,6 +18,8 @@ pub enum LexError {
MalformedChar(String),
/// Error in the script text.
InputError(String),
/// An identifier is in an invalid format.
MalformedIdentifier(String),
}
impl Error for LexError {}
@ -29,6 +31,9 @@ impl fmt::Display for LexError {
Self::MalformedEscapeSequence(s) => write!(f, "Invalid escape sequence: '{}'", s),
Self::MalformedNumber(s) => write!(f, "Invalid number: '{}'", s),
Self::MalformedChar(s) => write!(f, "Invalid character: '{}'", s),
Self::MalformedIdentifier(s) => {
write!(f, "Variable name is not in a legal format: '{}'", s)
}
Self::InputError(s) => write!(f, "{}", s),
Self::UnterminatedString => write!(f, "Open string is not terminated"),
}
@ -51,20 +56,27 @@ pub enum ParseErrorType {
/// An open `{` is missing the corresponding closing `}`.
MissingRightBrace(String),
/// An open `[` is missing the corresponding closing `]`.
#[cfg(not(feature = "no_index"))]
MissingRightBracket(String),
/// An expression in function call arguments `()` has syntax error.
MalformedCallExpr(String),
/// An expression in indexing brackets `[]` has syntax error.
#[cfg(not(feature = "no_index"))]
MalformedIndexExpr(String),
/// Invalid expression assigned to constant.
ForbiddenConstantExpr(String),
/// Missing a variable name after the `let` keyword.
VarExpectsIdentifier,
/// Missing a variable name after the `let`, `const` or `for` keywords.
VariableExpected,
/// A `for` statement is missing the `in` keyword.
MissingIn,
/// Defining a function `fn` in an appropriate place (e.g. inside another function).
#[cfg(not(feature = "no_function"))]
WrongFnDefinition,
/// Missing a function name after the `fn` keyword.
#[cfg(not(feature = "no_function"))]
FnMissingName,
/// A function definition is missing the parameters list. Wrapped value is the function name.
#[cfg(not(feature = "no_function"))]
FnMissingParams(String),
/// Assignment to an inappropriate LHS (left-hand-side) expression.
AssignmentToInvalidLHS,
@ -102,13 +114,19 @@ impl ParseError {
ParseErrorType::MissingRightParen(_) => "Expecting ')'",
ParseErrorType::MissingLeftBrace => "Expecting '{'",
ParseErrorType::MissingRightBrace(_) => "Expecting '}'",
#[cfg(not(feature = "no_index"))]
ParseErrorType::MissingRightBracket(_) => "Expecting ']'",
ParseErrorType::MalformedCallExpr(_) => "Invalid expression in function call arguments",
#[cfg(not(feature = "no_index"))]
ParseErrorType::MalformedIndexExpr(_) => "Invalid index in indexing expression",
ParseErrorType::ForbiddenConstantExpr(_) => "Expecting a constant",
ParseErrorType::VarExpectsIdentifier => "Expecting name of a variable",
ParseErrorType::MissingIn => "Expecting 'in'",
ParseErrorType::VariableExpected => "Expecting name of a variable",
#[cfg(not(feature = "no_function"))]
ParseErrorType::FnMissingName => "Expecting name in function declaration",
#[cfg(not(feature = "no_function"))]
ParseErrorType::FnMissingParams(_) => "Expecting parameters in function declaration",
#[cfg(not(feature = "no_function"))]
ParseErrorType::WrongFnDefinition => "Function definitions must be at top level and cannot be inside a block or another function",
ParseErrorType::AssignmentToInvalidLHS => "Cannot assign to this expression",
ParseErrorType::AssignmentToCopy => "Cannot assign to this expression because it will only be changing a copy of the value",
@ -122,21 +140,31 @@ 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)
| ParseErrorType::MalformedIndexExpr(ref s)
| ParseErrorType::MalformedCallExpr(ref s) => {
ParseErrorType::BadInput(ref s) | ParseErrorType::MalformedCallExpr(ref s) => {
write!(f, "{}", if s.is_empty() { self.desc() } else { s })?
}
ParseErrorType::ForbiddenConstantExpr(ref s) => {
write!(f, "Expecting a constant to assign to '{}'", s)?
}
ParseErrorType::UnknownOperator(ref s) => write!(f, "{}: '{}'", self.desc(), s)?,
#[cfg(not(feature = "no_index"))]
ParseErrorType::MalformedIndexExpr(ref s) => {
write!(f, "{}", if s.is_empty() { self.desc() } else { s })?
}
#[cfg(not(feature = "no_function"))]
ParseErrorType::FnMissingParams(ref s) => {
write!(f, "Expecting parameters for function '{}'", s)?
}
ParseErrorType::MissingRightParen(ref s)
| ParseErrorType::MissingRightBrace(ref s)
| ParseErrorType::MissingRightBracket(ref s) => write!(f, "{} for {}", self.desc(), s)?,
ParseErrorType::MissingRightParen(ref s) | ParseErrorType::MissingRightBrace(ref s) => {
write!(f, "{} for {}", self.desc(), s)?
}
#[cfg(not(feature = "no_index"))]
ParseErrorType::MissingRightBracket(ref s) => write!(f, "{} for {}", self.desc(), s)?,
ParseErrorType::AssignmentToConstant(ref s) if s.is_empty() => {
write!(f, "{}", self.desc())?
}

View File

@ -882,10 +882,11 @@ impl<'a> TokenIterator<'a> {
}
}
let out: String = result.iter().collect();
let has_letter = result.iter().any(char::is_ascii_alphabetic);
let identifier: String = result.iter().collect();
return Some((
match out.as_str() {
match identifier.as_str() {
"true" => Token::True,
"false" => Token::False,
"let" => Token::Let,
@ -903,7 +904,9 @@ impl<'a> TokenIterator<'a> {
#[cfg(not(feature = "no_function"))]
"fn" => Token::Fn,
_ => Token::Identifier(out),
_ if has_letter => Token::Identifier(identifier),
_ => Token::LexError(LERR::MalformedIdentifier(identifier)),
},
pos,
));
@ -1904,14 +1907,17 @@ fn parse_for<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, ParseE
let name = match input.next() {
Some((Token::Identifier(s), _)) => s,
Some((_, pos)) => return Err(ParseError::new(PERR::VarExpectsIdentifier, pos)),
None => return Err(ParseError::new(PERR::VarExpectsIdentifier, Position::eof())),
Some((Token::LexError(s), pos)) => {
return Err(ParseError::new(PERR::BadInput(s.to_string()), pos))
}
Some((_, pos)) => return Err(ParseError::new(PERR::VariableExpected, pos)),
None => return Err(ParseError::new(PERR::VariableExpected, Position::eof())),
};
match input.next() {
Some((Token::In, _)) => {}
Some((_, pos)) => return Err(ParseError::new(PERR::VarExpectsIdentifier, pos)),
None => return Err(ParseError::new(PERR::VarExpectsIdentifier, Position::eof())),
Some((Token::In, _)) => (),
Some((_, pos)) => return Err(ParseError::new(PERR::MissingIn, pos)),
None => return Err(ParseError::new(PERR::MissingIn, Position::eof())),
}
let expr = parse_expr(input)?;
@ -1932,8 +1938,11 @@ fn parse_var<'a>(
let name = match input.next() {
Some((Token::Identifier(s), _)) => s,
Some((_, pos)) => return Err(ParseError::new(PERR::VarExpectsIdentifier, pos)),
None => return Err(ParseError::new(PERR::VarExpectsIdentifier, Position::eof())),
Some((Token::LexError(s), pos)) => {
return Err(ParseError::new(PERR::BadInput(s.to_string()), pos))
}
Some((_, pos)) => return Err(ParseError::new(PERR::VariableExpected, pos)),
None => return Err(ParseError::new(PERR::VariableExpected, Position::eof())),
};
if matches!(input.peek(), Some(&(Token::Equals, _))) {