From 2c90fea76487dfab6c394a32a4baa83028f2080a Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 16 Mar 2020 12:38:01 +0800 Subject: [PATCH] Catch illegal variable names. --- src/error.rs | 46 +++++++++++++++++++++++++++++++++++++--------- src/parser.rs | 29 +++++++++++++++++++---------- 2 files changed, 56 insertions(+), 19 deletions(-) diff --git a/src/error.rs b/src/error.rs index 89d6f370..a582bcb2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -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())? } diff --git a/src/parser.rs b/src/parser.rs index d87d351b..366329c3 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -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>) -> Result 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, _))) {