diff --git a/kalcy/include/kalcy/error.hpp b/kalcy/include/kalcy/error.hpp index e6775e7..4ee3436 100644 --- a/kalcy/include/kalcy/error.hpp +++ b/kalcy/include/kalcy/error.hpp @@ -21,6 +21,13 @@ struct InternalError : Error { using Error::Error; }; +/// +/// \brief Unrecognized token. +/// +struct UnrecognizedToken : Error { + explicit UnrecognizedToken(Token token); +}; + /// /// \brief Parse error. /// diff --git a/kalcy/src/error.cpp b/kalcy/src/error.cpp index 3b63826..769cf03 100644 --- a/kalcy/src/error.cpp +++ b/kalcy/src/error.cpp @@ -11,6 +11,8 @@ auto Error::build_highlight(char const higlight) const -> std::string { return ret.str(); } +UnrecognizedToken::UnrecognizedToken(Token const token) : Error(token, std::format("unrecognized token: '{}'", token.lexeme)) {} + ParseError::ParseError(Token const token) : Error(token, "parse error") {} ParseError::ParseError(Token const token, Token::Type const expected) diff --git a/kalcy/src/scanner.cpp b/kalcy/src/scanner.cpp index 20382d9..f821123 100644 --- a/kalcy/src/scanner.cpp +++ b/kalcy/src/scanner.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -28,7 +29,7 @@ auto Scanner::next() -> Token { if (match_number(out)) { return out; } if (match_identifier(out)) { return out; } - return {}; + throw UnrecognizedToken{make_token(Token::Type::eNone, {&m_text.front(), 1})}; } auto Scanner::match_single(Token& out) -> bool { diff --git a/tests/tests/test_parser.cpp b/tests/tests/test_parser.cpp index 1811308..d6482ca 100644 --- a/tests/tests/test_parser.cpp +++ b/tests/tests/test_parser.cpp @@ -97,6 +97,28 @@ ADD_TEST(ParseErrorOnExtraneous) { EXPECT(did_throw); } +ADD_TEST(ParseErrorOnInvalidToken) { + bool did_throw{}; + try { + parse("`"); + } catch (UnrecognizedToken const& e) { + EXPECT(e.token.location == 0); + EXPECT(e.token.lexeme == "`"); + did_throw = true; + } + EXPECT(did_throw); + + did_throw = false; + try { + parse("pi@"); + } catch (UnrecognizedToken const& e) { + EXPECT(e.token.location == 2); + EXPECT(e.token.lexeme == "@"); + did_throw = true; + } + EXPECT(did_throw); +} + ADD_TEST(ParsePrecedence) { auto result = parse("1 + 2 * 3"); ASSERT(result != nullptr);