Error reporting of use of non-symbols in indentifier context
This commit is contained in:
parent
0a00080d2d
commit
1e1d9b0cb6
@ -119,7 +119,8 @@ std::ostream& operator<<(std::ostream& os, Error const& err)
|
|||||||
[](errors::Unexpected_Keyword const&) { return "Unexpected keyword"; },
|
[](errors::Unexpected_Keyword const&) { return "Unexpected keyword"; },
|
||||||
[](errors::Unrecognized_Character const&) { return "Unrecognized character"; },
|
[](errors::Unrecognized_Character const&) { return "Unrecognized character"; },
|
||||||
[](errors::internal::Unexpected_Token const&) { return "Unexpected token"; },
|
[](errors::internal::Unexpected_Token const&) { return "Unexpected token"; },
|
||||||
[](errors::Expected_Expression_Separator_Before const&) { return "Missing semicolon"; }
|
[](errors::Expected_Expression_Separator_Before const&) { return "Missing semicolon"; },
|
||||||
|
[](errors::Literal_As_Identifier const&) { return "Literal used in place of an identifier"; }
|
||||||
}, err.details);
|
}, err.details);
|
||||||
|
|
||||||
error_heading(os, err.location, Error_Level::Error, short_description);
|
error_heading(os, err.location, Error_Level::Error, short_description);
|
||||||
@ -161,6 +162,20 @@ std::ostream& operator<<(std::ostream& os, Error const& err)
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
[&os](errors::Literal_As_Identifier const& err) {
|
||||||
|
os << "I expected an identifier in " << err.context << ", but found" << (err.type_name.empty() ? "" : " ") << err.type_name << " value = '" << err.source << "'\n";
|
||||||
|
|
||||||
|
|
||||||
|
if (err.type_name == "chord") {
|
||||||
|
os << "\nTry renaming to different name or appending with something that is not part of chord literal like 'x'\n";
|
||||||
|
|
||||||
|
os << pretty::begin_comment <<
|
||||||
|
"\nMusical notation names are reserved for chord and note notations,\n"
|
||||||
|
"and cannot be reused as an identifier to prevent ambiguity\n"
|
||||||
|
<< pretty::end;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
[&os](errors::Not_Callable const&) { unimplemented(); },
|
[&os](errors::Not_Callable const&) { unimplemented(); },
|
||||||
[&os](errors::Undefined_Operator const&) { unimplemented(); },
|
[&os](errors::Undefined_Operator const&) { unimplemented(); },
|
||||||
[&os](errors::Unexpected_Keyword const&) { unimplemented(); },
|
[&os](errors::Unexpected_Keyword const&) { unimplemented(); },
|
||||||
|
@ -83,6 +83,14 @@ namespace errors
|
|||||||
std::string_view type;
|
std::string_view type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// When user provides literal where identifier should be
|
||||||
|
struct Literal_As_Identifier
|
||||||
|
{
|
||||||
|
std::string_view type_name;
|
||||||
|
std::string_view source;
|
||||||
|
std::string_view context;
|
||||||
|
};
|
||||||
|
|
||||||
/// Collection of messages that are considered internal and should not be printed to the end user.
|
/// Collection of messages that are considered internal and should not be printed to the end user.
|
||||||
namespace internal
|
namespace internal
|
||||||
{
|
{
|
||||||
@ -109,6 +117,7 @@ namespace errors
|
|||||||
Unexpected_Empty_Source,
|
Unexpected_Empty_Source,
|
||||||
Unexpected_Keyword,
|
Unexpected_Keyword,
|
||||||
Unrecognized_Character,
|
Unrecognized_Character,
|
||||||
|
Literal_As_Identifier,
|
||||||
internal::Unexpected_Token
|
internal::Unexpected_Token
|
||||||
>;
|
>;
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,18 @@
|
|||||||
|
|
||||||
static Ast wrap_if_several(std::vector<Ast> &&ast, Ast(*wrapper)(std::vector<Ast>));
|
static Ast wrap_if_several(std::vector<Ast> &&ast, Ast(*wrapper)(std::vector<Ast>));
|
||||||
|
|
||||||
|
static inline bool is_identifier_looking(Token::Type type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case Token::Type::Symbol:
|
||||||
|
case Token::Type::Keyword:
|
||||||
|
case Token::Type::Chord:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
constexpr auto Literal_Keywords = std::array {
|
constexpr auto Literal_Keywords = std::array {
|
||||||
"false"sv,
|
"false"sv,
|
||||||
"nil"sv,
|
"nil"sv,
|
||||||
@ -39,7 +51,7 @@ Result<Ast> Parser::parse(std::string_view source, std::string_view filename)
|
|||||||
|
|
||||||
auto const result = parser.parse_sequence();
|
auto const result = parser.parse_sequence();
|
||||||
|
|
||||||
if (parser.token_id < parser.tokens.size()) {
|
if (result.has_value() && parser.token_id < parser.tokens.size()) {
|
||||||
if (parser.expect(Token::Type::Keyword, "var")) {
|
if (parser.expect(Token::Type::Keyword, "var")) {
|
||||||
return Error {
|
return Error {
|
||||||
.details = errors::Expected_Expression_Separator_Before { .what = "var" },
|
.details = errors::Expected_Expression_Separator_Before { .what = "var" },
|
||||||
@ -71,14 +83,29 @@ Result<Ast> Parser::parse_variable_declaration()
|
|||||||
assert(expect(Token::Type::Keyword, "var"), "Parser::parse_variable_declaration must be called only on expressions that starts with 'var'");
|
assert(expect(Token::Type::Keyword, "var"), "Parser::parse_variable_declaration must be called only on expressions that starts with 'var'");
|
||||||
auto var = consume();
|
auto var = consume();
|
||||||
|
|
||||||
auto lvalue = Try(parse_many(*this, &Parser::parse_identifier, std::nullopt, At_Least::One));
|
auto lvalue = parse_many(*this, &Parser::parse_identifier, std::nullopt, At_Least::One);
|
||||||
|
if (not lvalue.has_value()) {
|
||||||
|
auto details = lvalue.error().details;
|
||||||
|
if (auto ut = std::get_if<errors::internal::Unexpected_Token>(&details); ut) {
|
||||||
|
return Error {
|
||||||
|
.details = errors::Literal_As_Identifier {
|
||||||
|
.type_name = ut->type,
|
||||||
|
.source = ut->source,
|
||||||
|
.context = "variable declaration"
|
||||||
|
},
|
||||||
|
.location = lvalue.error().location
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return std::move(lvalue).error();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (expect(Token::Type::Operator, "=")) {
|
if (expect(Token::Type::Operator, "=")) {
|
||||||
consume();
|
consume();
|
||||||
return Ast::variable_declaration(var.location, std::move(lvalue), Try(parse_expression()));
|
return Ast::variable_declaration(var.location, *std::move(lvalue), Try(parse_expression()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ast::variable_declaration(var.location, std::move(lvalue), std::nullopt);
|
return Ast::variable_declaration(var.location, *std::move(lvalue), std::nullopt);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<Ast> Parser::parse_infix_expression()
|
Result<Ast> Parser::parse_infix_expression()
|
||||||
@ -143,7 +170,35 @@ Result<Ast> Parser::parse_atomic_expression()
|
|||||||
|
|
||||||
return parse_sequence().and_then([&](Ast &&ast) -> Result<Ast> {
|
return parse_sequence().and_then([&](Ast &&ast) -> Result<Ast> {
|
||||||
if (not expect(Token::Type::Close_Block)) {
|
if (not expect(Token::Type::Close_Block)) {
|
||||||
unimplemented("Error handling of this code is not implemented yet");
|
if (expect(Token::Type::Parameter_Separator)) {
|
||||||
|
if (is_lambda) {
|
||||||
|
assert(false, "There should be error message that you cannot put multiple parameter separators in one block");
|
||||||
|
} else {
|
||||||
|
// This may be a result of user trying to specify parameters from things that cannot be parameters (like chord literals)
|
||||||
|
// or accidential hit of "|" on the keyboard. We can detect first case by ensuring that all tokens between this place
|
||||||
|
// and beggining of a block are identifier looking: keywords, symbols, literal chord declarations, boolean literals etc
|
||||||
|
std::optional<Token> invalid_token = std::nullopt;
|
||||||
|
auto const success = std::all_of(tokens.begin() + start, tokens.begin() + (token_id - start + 1), [&](Token const& token) {
|
||||||
|
if (!invalid_token && token.type != Token::Type::Symbol) {
|
||||||
|
// TODO Maybe gather all tokens to provide all the help needed in the one iteration
|
||||||
|
invalid_token = token;
|
||||||
|
}
|
||||||
|
return is_identifier_looking(token.type);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (success && invalid_token) {
|
||||||
|
return Error {
|
||||||
|
.details = errors::Literal_As_Identifier {
|
||||||
|
.type_name = type_name(invalid_token->type),
|
||||||
|
.source = invalid_token->source,
|
||||||
|
.context = "block parameter list"
|
||||||
|
},
|
||||||
|
.location = invalid_token->location
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unimplemented("Don't know why it stopped, maybe end of file?");
|
||||||
}
|
}
|
||||||
consume();
|
consume();
|
||||||
if (is_lambda) {
|
if (is_lambda) {
|
||||||
@ -182,7 +237,7 @@ Result<Ast> Parser::parse_identifier_with_trailing_separators()
|
|||||||
// TODO Specific error message
|
// TODO Specific error message
|
||||||
return Error {
|
return Error {
|
||||||
.details = errors::internal::Unexpected_Token {
|
.details = errors::internal::Unexpected_Token {
|
||||||
.type = "", // TODO fill type
|
.type = type_name(peek()->type),
|
||||||
.source = peek()->source,
|
.source = peek()->source,
|
||||||
.when = "identifier parsing"
|
.when = "identifier parsing"
|
||||||
},
|
},
|
||||||
@ -200,7 +255,7 @@ Result<Ast> Parser::parse_identifier()
|
|||||||
// TODO Specific error message
|
// TODO Specific error message
|
||||||
return Error {
|
return Error {
|
||||||
.details = errors::internal::Unexpected_Token {
|
.details = errors::internal::Unexpected_Token {
|
||||||
.type = "", // TODO fill type
|
.type = type_name(peek()->type),
|
||||||
.source = peek()->source,
|
.source = peek()->source,
|
||||||
.when = "identifier parsing"
|
.when = "identifier parsing"
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user