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), MalformedChar(String),
/// Error in the script text. /// Error in the script text.
InputError(String), InputError(String),
/// An identifier is in an invalid format.
MalformedIdentifier(String),
} }
impl Error for LexError {} impl Error for LexError {}
@ -29,6 +31,9 @@ impl fmt::Display for LexError {
Self::MalformedEscapeSequence(s) => write!(f, "Invalid escape sequence: '{}'", s), Self::MalformedEscapeSequence(s) => write!(f, "Invalid escape sequence: '{}'", s),
Self::MalformedNumber(s) => write!(f, "Invalid number: '{}'", s), Self::MalformedNumber(s) => write!(f, "Invalid number: '{}'", s),
Self::MalformedChar(s) => write!(f, "Invalid character: '{}'", 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::InputError(s) => write!(f, "{}", s),
Self::UnterminatedString => write!(f, "Open string is not terminated"), Self::UnterminatedString => write!(f, "Open string is not terminated"),
} }
@ -51,20 +56,27 @@ pub enum ParseErrorType {
/// An open `{` is missing the corresponding closing `}`. /// An open `{` is missing the corresponding closing `}`.
MissingRightBrace(String), MissingRightBrace(String),
/// An open `[` is missing the corresponding closing `]`. /// An open `[` is missing the corresponding closing `]`.
#[cfg(not(feature = "no_index"))]
MissingRightBracket(String), MissingRightBracket(String),
/// An expression in function call arguments `()` has syntax error. /// An expression in function call arguments `()` has syntax error.
MalformedCallExpr(String), MalformedCallExpr(String),
/// An expression in indexing brackets `[]` has syntax error. /// An expression in indexing brackets `[]` has syntax error.
#[cfg(not(feature = "no_index"))]
MalformedIndexExpr(String), MalformedIndexExpr(String),
/// Invalid expression assigned to constant. /// Invalid expression assigned to constant.
ForbiddenConstantExpr(String), ForbiddenConstantExpr(String),
/// Missing a variable name after the `let` keyword. /// Missing a variable name after the `let`, `const` or `for` keywords.
VarExpectsIdentifier, VariableExpected,
/// A `for` statement is missing the `in` keyword.
MissingIn,
/// Defining a function `fn` in an appropriate place (e.g. inside another function). /// Defining a function `fn` in an appropriate place (e.g. inside another function).
#[cfg(not(feature = "no_function"))]
WrongFnDefinition, WrongFnDefinition,
/// Missing a function name after the `fn` keyword. /// Missing a function name after the `fn` keyword.
#[cfg(not(feature = "no_function"))]
FnMissingName, FnMissingName,
/// A function definition is missing the parameters list. Wrapped value is the function name. /// A function definition is missing the parameters list. Wrapped value is the function name.
#[cfg(not(feature = "no_function"))]
FnMissingParams(String), FnMissingParams(String),
/// Assignment to an inappropriate LHS (left-hand-side) expression. /// Assignment to an inappropriate LHS (left-hand-side) expression.
AssignmentToInvalidLHS, AssignmentToInvalidLHS,
@ -102,13 +114,19 @@ impl ParseError {
ParseErrorType::MissingRightParen(_) => "Expecting ')'", ParseErrorType::MissingRightParen(_) => "Expecting ')'",
ParseErrorType::MissingLeftBrace => "Expecting '{'", ParseErrorType::MissingLeftBrace => "Expecting '{'",
ParseErrorType::MissingRightBrace(_) => "Expecting '}'", ParseErrorType::MissingRightBrace(_) => "Expecting '}'",
#[cfg(not(feature = "no_index"))]
ParseErrorType::MissingRightBracket(_) => "Expecting ']'", ParseErrorType::MissingRightBracket(_) => "Expecting ']'",
ParseErrorType::MalformedCallExpr(_) => "Invalid expression in function call arguments", ParseErrorType::MalformedCallExpr(_) => "Invalid expression in function call arguments",
#[cfg(not(feature = "no_index"))]
ParseErrorType::MalformedIndexExpr(_) => "Invalid index in indexing expression", ParseErrorType::MalformedIndexExpr(_) => "Invalid index in indexing expression",
ParseErrorType::ForbiddenConstantExpr(_) => "Expecting a constant", 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", ParseErrorType::FnMissingName => "Expecting name in function declaration",
#[cfg(not(feature = "no_function"))]
ParseErrorType::FnMissingParams(_) => "Expecting parameters in function declaration", 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::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::AssignmentToInvalidLHS => "Cannot assign to this expression",
ParseErrorType::AssignmentToCopy => "Cannot assign to this expression because it will only be changing a copy of the value", 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 { impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 { match self.0 {
ParseErrorType::BadInput(ref s) ParseErrorType::BadInput(ref s) | ParseErrorType::MalformedCallExpr(ref s) => {
| ParseErrorType::MalformedIndexExpr(ref s)
| ParseErrorType::MalformedCallExpr(ref s) => {
write!(f, "{}", if s.is_empty() { self.desc() } else { s })? write!(f, "{}", if s.is_empty() { self.desc() } else { s })?
} }
ParseErrorType::ForbiddenConstantExpr(ref s) => { ParseErrorType::ForbiddenConstantExpr(ref s) => {
write!(f, "Expecting a constant to assign to '{}'", s)? write!(f, "Expecting a constant to assign to '{}'", s)?
} }
ParseErrorType::UnknownOperator(ref s) => write!(f, "{}: '{}'", self.desc(), 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) => { ParseErrorType::FnMissingParams(ref s) => {
write!(f, "Expecting parameters for function '{}'", s)? write!(f, "Expecting parameters for function '{}'", s)?
} }
ParseErrorType::MissingRightParen(ref s)
| ParseErrorType::MissingRightBrace(ref s) ParseErrorType::MissingRightParen(ref s) | ParseErrorType::MissingRightBrace(ref s) => {
| ParseErrorType::MissingRightBracket(ref s) => write!(f, "{} for {}", self.desc(), 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() => { ParseErrorType::AssignmentToConstant(ref s) if s.is_empty() => {
write!(f, "{}", self.desc())? 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(( return Some((
match out.as_str() { match identifier.as_str() {
"true" => Token::True, "true" => Token::True,
"false" => Token::False, "false" => Token::False,
"let" => Token::Let, "let" => Token::Let,
@ -903,7 +904,9 @@ impl<'a> TokenIterator<'a> {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
"fn" => Token::Fn, "fn" => Token::Fn,
_ => Token::Identifier(out), _ if has_letter => Token::Identifier(identifier),
_ => Token::LexError(LERR::MalformedIdentifier(identifier)),
}, },
pos, pos,
)); ));
@ -1904,14 +1907,17 @@ fn parse_for<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, ParseE
let name = match input.next() { let name = match input.next() {
Some((Token::Identifier(s), _)) => s, Some((Token::Identifier(s), _)) => s,
Some((_, pos)) => return Err(ParseError::new(PERR::VarExpectsIdentifier, pos)), Some((Token::LexError(s), pos)) => {
None => return Err(ParseError::new(PERR::VarExpectsIdentifier, Position::eof())), 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() { match input.next() {
Some((Token::In, _)) => {} Some((Token::In, _)) => (),
Some((_, pos)) => return Err(ParseError::new(PERR::VarExpectsIdentifier, pos)), Some((_, pos)) => return Err(ParseError::new(PERR::MissingIn, pos)),
None => return Err(ParseError::new(PERR::VarExpectsIdentifier, Position::eof())), None => return Err(ParseError::new(PERR::MissingIn, Position::eof())),
} }
let expr = parse_expr(input)?; let expr = parse_expr(input)?;
@ -1932,8 +1938,11 @@ fn parse_var<'a>(
let name = match input.next() { let name = match input.next() {
Some((Token::Identifier(s), _)) => s, Some((Token::Identifier(s), _)) => s,
Some((_, pos)) => return Err(ParseError::new(PERR::VarExpectsIdentifier, pos)), Some((Token::LexError(s), pos)) => {
None => return Err(ParseError::new(PERR::VarExpectsIdentifier, Position::eof())), 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, _))) { if matches!(input.peek(), Some(&(Token::Equals, _))) {