diff --git a/README.md b/README.md index c0931fa6..7bea89bb 100644 --- a/README.md +++ b/README.md @@ -2480,7 +2480,7 @@ engine.set_max_string_size(500); // allow strings only up to 500 byte engine.set_max_string_size(0); // allow unlimited string length ``` -A script attempting to create a string literal longer than the maximum will terminate with a parse error. +A script attempting to create a string literal longer than the maximum length will terminate with a parse error. Any script operation that produces a string longer than the maximum also terminates the script with an error result. This check can be disabled via the [`unchecked`] feature for higher performance (but higher risks as well). diff --git a/src/error.rs b/src/error.rs index 3d4c47c7..ce8615ed 100644 --- a/src/error.rs +++ b/src/error.rs @@ -22,8 +22,8 @@ pub enum LexError { MalformedChar(String), /// An identifier is in an invalid format. MalformedIdentifier(String), - /// Bad keyword encountered when tokenizing the script text. - ImproperKeyword(String), + /// Bad symbol encountered when tokenizing the script text. + ImproperSymbol(String), } impl Error for LexError {} @@ -42,11 +42,18 @@ impl fmt::Display for LexError { "Length of string literal exceeds the maximum limit ({})", max ), - Self::ImproperKeyword(s) => write!(f, "{}", s), + Self::ImproperSymbol(s) => write!(f, "{}", s), } } } +impl LexError { + /// Convert a `LexError` into a `ParseError`. + pub fn into_err(&self, pos: Position) -> ParseError { + ParseError(Box::new(self.into()), pos) + } +} + /// Type of error encountered when parsing a script. /// /// Some errors never appear when certain features are turned on. @@ -217,6 +224,17 @@ impl fmt::Display for ParseErrorType { } } +impl From<&LexError> for ParseErrorType { + fn from(err: &LexError) -> Self { + match err { + LexError::StringTooLong(max) => { + Self::LiteralTooLarge("Length of string literal".to_string(), *max) + } + _ => Self::BadInput(err.to_string()), + } + } +} + /// Error when parsing a script. #[derive(Debug, Eq, PartialEq, Clone, Hash)] pub struct ParseError(pub Box, pub Position); diff --git a/src/parser.rs b/src/parser.rs index ec99adaf..acbb59e5 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -767,7 +767,7 @@ fn parse_paren_expr( // ( xxx ) (Token::RightParen, _) => Ok(expr), // ( - (Token::LexError(err), pos) => return Err(PERR::BadInput(err.to_string()).into_err(pos)), + (Token::LexError(err), pos) => return Err(err.into_err(pos)), // ( xxx ??? (_, pos) => Err(PERR::MissingToken( Token::RightParen.into(), @@ -800,7 +800,7 @@ fn parse_call_expr( .into_err(settings.pos)) } // id - Token::LexError(err) => return Err(PERR::BadInput(err.to_string()).into_err(settings.pos)), + Token::LexError(err) => return Err(err.into_err(settings.pos)), // id() Token::RightParen => { eat_token(input, Token::RightParen); @@ -880,9 +880,7 @@ fn parse_call_expr( .into_err(*pos)) } // id(...args - (Token::LexError(err), pos) => { - return Err(PERR::BadInput(err.to_string()).into_err(*pos)) - } + (Token::LexError(err), pos) => return Err(err.into_err(*pos)), // id(...args ??? (_, pos) => { return Err(PERR::MissingToken( @@ -1065,7 +1063,7 @@ fn parse_index_chain( } } } - (Token::LexError(err), pos) => return Err(PERR::BadInput(err.to_string()).into_err(*pos)), + (Token::LexError(err), pos) => return Err(err.into_err(*pos)), (_, pos) => Err(PERR::MissingToken( Token::RightBracket.into(), "for a matching [ in this index expression".into(), @@ -1110,9 +1108,7 @@ fn parse_array_literal( ) .into_err(*pos)) } - (Token::LexError(err), pos) => { - return Err(PERR::BadInput(err.to_string()).into_err(*pos)) - } + (Token::LexError(err), pos) => return Err(err.into_err(*pos)), (_, pos) => { return Err(PERR::MissingToken( Token::Comma.into(), @@ -1144,9 +1140,7 @@ fn parse_map_literal( let (name, pos) = match input.next().unwrap() { (Token::Identifier(s), pos) => (s, pos), (Token::StringConst(s), pos) => (s, pos), - (Token::LexError(err), pos) => { - return Err(PERR::BadInput(err.to_string()).into_err(pos)) - } + (Token::LexError(err), pos) => return Err(err.into_err(pos)), (_, pos) if map.is_empty() => { return Err( PERR::MissingToken(Token::RightBrace.into(), MISSING_RBRACE.into()) @@ -1164,9 +1158,7 @@ fn parse_map_literal( match input.next().unwrap() { (Token::Colon, _) => (), - (Token::LexError(err), pos) => { - return Err(PERR::BadInput(err.to_string()).into_err(pos)) - } + (Token::LexError(err), pos) => return Err(err.into_err(pos)), (_, pos) => { return Err(PERR::MissingToken( Token::Colon.into(), @@ -1205,9 +1197,7 @@ fn parse_map_literal( ) .into_err(*pos)) } - (Token::LexError(err), pos) => { - return Err(PERR::BadInput(err.to_string()).into_err(*pos)) - } + (Token::LexError(err), pos) => return Err(err.into_err(*pos)), (_, pos) => { return Err( PERR::MissingToken(Token::RightBrace.into(), MISSING_RBRACE.into()) @@ -1269,7 +1259,7 @@ fn parse_primary( Token::MapStart => parse_map_literal(input, state, settings.level_up())?, Token::True => Expr::True(settings.pos), Token::False => Expr::False(settings.pos), - Token::LexError(err) => return Err(PERR::BadInput(err.to_string()).into_err(settings.pos)), + Token::LexError(err) => return Err(err.into_err(settings.pos)), _ => { return Err( PERR::BadInput(format!("Unexpected '{}'", token.syntax())).into_err(settings.pos) @@ -1380,12 +1370,7 @@ fn parse_unary( None } }) - .ok_or_else(|| { - PERR::BadInput( - LexError::MalformedNumber(format!("-{}", x.0)).to_string(), - ) - .into_err(pos) - }) + .ok_or_else(|| LexError::MalformedNumber(format!("-{}", x.0)).into_err(pos)) } // Negative float @@ -1990,7 +1975,7 @@ fn parse_for( // Variable name (Token::Identifier(s), _) => s, // Bad identifier - (Token::LexError(err), pos) => return Err(PERR::BadInput(err.to_string()).into_err(pos)), + (Token::LexError(err), pos) => return Err(err.into_err(pos)), // EOF (Token::EOF, pos) => return Err(PERR::VariableExpected.into_err(pos)), // Not a variable name @@ -2000,7 +1985,7 @@ fn parse_for( // for name in ... match input.next().unwrap() { (Token::In, _) => (), - (Token::LexError(err), pos) => return Err(PERR::BadInput(err.to_string()).into_err(pos)), + (Token::LexError(err), pos) => return Err(err.into_err(pos)), (_, pos) => { return Err( PERR::MissingToken(Token::In.into(), "after the iteration variable".into()) @@ -2038,7 +2023,7 @@ fn parse_let( // let name ... let (name, pos) = match input.next().unwrap() { (Token::Identifier(s), pos) => (s, pos), - (Token::LexError(err), pos) => return Err(PERR::BadInput(err.to_string()).into_err(pos)), + (Token::LexError(err), pos) => return Err(err.into_err(pos)), (_, pos) => return Err(PERR::VariableExpected.into_err(pos)), }; @@ -2109,7 +2094,7 @@ fn parse_import( // import expr as name ... let (name, _) = match input.next().unwrap() { (Token::Identifier(s), pos) => (s, pos), - (Token::LexError(err), pos) => return Err(PERR::BadInput(err.to_string()).into_err(pos)), + (Token::LexError(err), pos) => return Err(err.into_err(pos)), (_, pos) => return Err(PERR::VariableExpected.into_err(pos)), }; @@ -2132,9 +2117,7 @@ fn parse_export( loop { let (id, id_pos) = match input.next().unwrap() { (Token::Identifier(s), pos) => (s.clone(), pos), - (Token::LexError(err), pos) => { - return Err(PERR::BadInput(err.to_string()).into_err(pos)) - } + (Token::LexError(err), pos) => return Err(err.into_err(pos)), (_, pos) => return Err(PERR::VariableExpected.into_err(pos)), }; @@ -2189,7 +2172,7 @@ fn parse_block( // Must start with { settings.pos = match input.next().unwrap() { (Token::LeftBrace, pos) => pos, - (Token::LexError(err), pos) => return Err(PERR::BadInput(err.to_string()).into_err(pos)), + (Token::LexError(err), pos) => return Err(err.into_err(pos)), (_, pos) => { return Err(PERR::MissingToken( Token::LeftBrace.into(), @@ -2229,9 +2212,7 @@ fn parse_block( // { ... { stmt } ??? (_, _) if !need_semicolon => (), // { ... stmt - (Token::LexError(err), pos) => { - return Err(PERR::BadInput(err.to_string()).into_err(*pos)) - } + (Token::LexError(err), pos) => return Err(err.into_err(*pos)), // { ... stmt ??? (_, pos) => { // Semicolons are not optional between statements @@ -2380,9 +2361,7 @@ fn parse_fn( state.push((s.clone(), ScopeEntryType::Normal)); params.push((s, pos)) } - (Token::LexError(err), pos) => { - return Err(PERR::BadInput(err.to_string()).into_err(pos)) - } + (Token::LexError(err), pos) => return Err(err.into_err(pos)), (_, pos) => { return Err(PERR::MissingToken(Token::RightParen.into(), end_err).into_err(pos)) } @@ -2394,9 +2373,7 @@ fn parse_fn( (Token::Identifier(_), pos) => { return Err(PERR::MissingToken(Token::Comma.into(), sep_err).into_err(pos)) } - (Token::LexError(err), pos) => { - return Err(PERR::BadInput(err.to_string()).into_err(pos)) - } + (Token::LexError(err), pos) => return Err(err.into_err(pos)), (_, pos) => { return Err(PERR::MissingToken(Token::Comma.into(), sep_err).into_err(pos)) } @@ -2567,9 +2544,7 @@ impl Engine { // { stmt } ??? (_, _) if !need_semicolon => (), // stmt - (Token::LexError(err), pos) => { - return Err(PERR::BadInput(err.to_string()).into_err(*pos)) - } + (Token::LexError(err), pos) => return Err(err.into_err(*pos)), // stmt ??? (_, pos) => { // Semicolons are not optional between statements diff --git a/tests/data_size.rs b/tests/data_size.rs index 77e660fa..7884971f 100644 --- a/tests/data_size.rs +++ b/tests/data_size.rs @@ -14,12 +14,12 @@ fn test_max_string_size() -> Result<(), Box> { assert!(matches!( engine.compile(r#"let x = "hello, world!";"#).expect_err("should error"), - ParseError(x, _) if *x == ParseErrorType::BadInput("Length of string literal exceeds the maximum limit (10)".to_string()) + ParseError(x, _) if *x == ParseErrorType::LiteralTooLarge("Length of string literal".to_string(), 10) )); assert!(matches!( engine.compile(r#"let x = "朝に紅顔、暮に白骨";"#).expect_err("should error"), - ParseError(x, _) if *x == ParseErrorType::BadInput("Length of string literal exceeds the maximum limit (10)".to_string()) + ParseError(x, _) if *x == ParseErrorType::LiteralTooLarge("Length of string literal".to_string(), 10) )); assert!(matches!(