From 9c0b1bca87ce36d68e80c731398b74da8648e511 Mon Sep 17 00:00:00 2001 From: jmd Date: Fri, 17 Nov 2023 22:40:13 -0800 Subject: [PATCH] [script] merge diagnostic functions in Context.. It will be simpler for subclasses later.. --- lib/nw/script/AstResolver.hpp | 163 +++++++++++++++++++++------------- lib/nw/script/Context.cpp | 85 ++++++++++-------- lib/nw/script/Context.hpp | 11 +-- lib/nw/script/NssLexer.cpp | 14 +-- lib/nw/script/NssParser.cpp | 44 ++++----- lib/nw/script/NssParser.hpp | 10 +-- 6 files changed, 185 insertions(+), 142 deletions(-) diff --git a/lib/nw/script/AstResolver.hpp b/lib/nw/script/AstResolver.hpp index e1ac1a549..99499a2ac 100644 --- a/lib/nw/script/AstResolver.hpp +++ b/lib/nw/script/AstResolver.hpp @@ -60,8 +60,9 @@ struct AstResolver : BaseVisitor { if (it != std::end(scope_stack_.back())) { if (is_type) { if (it->second.struct_decl) { - ctx_->semantic_error(parent_, + ctx_->semantic_diagnostic(parent_, fmt::format("declaring '{}' in the same scope twice", token.loc.view()), + false, token.loc); } else { it->second.struct_decl = dynamic_cast(decl); @@ -73,8 +74,9 @@ struct AstResolver : BaseVisitor { && dynamic_cast(decl)) { it->second.decl = decl; } else { - ctx_->semantic_error(parent_, + ctx_->semantic_diagnostic(parent_, fmt::format("declaring '{}' in the same scope twice", token.loc.view()), + false, token.loc); } } @@ -92,8 +94,9 @@ struct AstResolver : BaseVisitor { auto s = std::string(token.loc.view()); auto it = scope_stack_.back().find(s); if (it == std::end(scope_stack_.back())) { - ctx_->semantic_error(parent_, + ctx_->semantic_diagnostic(parent_, fmt::format("defining unknown variable '{}'", token.loc.view()), + false, token.loc); } if (is_type) { @@ -150,15 +153,17 @@ struct AstResolver : BaseVisitor { if (it == std::end(map)) { continue; } if (is_type) { if (!it->second.struct_decl_ready) { - ctx_->semantic_error(parent_, + ctx_->semantic_diagnostic(parent_, fmt::format("recursive use of struct '{}' in declaration", token), + false, loc); } return it->second.struct_decl; } else { if (it->second.decl && !it->second.decl_ready) { - ctx_->semantic_error(parent_, + ctx_->semantic_diagnostic(parent_, fmt::format("using declared variable '{}' in init", token), + false, loc); } return it->second.decl; @@ -196,17 +201,21 @@ struct AstResolver : BaseVisitor { // If there's a function declaration, try to match if (def->type_id_ != decl->type_id_) { - ctx_->semantic_error(parent_, - fmt::format("function declared with return type '{}', defined with return type '{}'", + ctx_->semantic_diagnostic(parent_, + fmt::format("function declared with return type '{}', defined with return type '{}' ", ctx_->type_name(decl->type_id_), - ctx_->type_name(def->type_id_))); + ctx_->type_name(def->type_id_)), + false, + def->type.type_specifier.loc); // [TODO] expand this } if (decl->params.size() != def->params.size()) { - ctx_->semantic_error(parent_, - fmt::format("function declared with parameter count '{}', defined with parameter count '{}'", + ctx_->semantic_diagnostic(parent_, + fmt::format("function declared with parameter count '{}', defined with parameter count '{}' ", decl->params.size(), - def->params.size())); + def->params.size()), + false, + def->type.type_specifier.loc); // [TODO] expand this } else { std::string reason; @@ -251,11 +260,7 @@ struct AstResolver : BaseVisitor { } } if (mismatch) { - if (warning) { - ctx_->semantic_warning(parent_, reason, loc); - } else { - ctx_->semantic_error(parent_, reason, loc); - } + ctx_->semantic_diagnostic(parent_, reason, warning, loc); } } } @@ -275,7 +280,8 @@ struct AstResolver : BaseVisitor { for (auto& p : decl->params) { p->accept(this); if (p->init && !p->init->is_const_) { - ctx_->semantic_error(parent_, "initializing parameter a with non-constant expression", + ctx_->semantic_diagnostic(parent_, "initializing parameter a with non-constant expression", + false, p->identifier.loc); } } @@ -300,7 +306,8 @@ struct AstResolver : BaseVisitor { for (auto& p : decl->decl_inline->params) { p->accept(this); if (p->init && !p->init->is_const_) { - ctx_->semantic_error(parent_, "initializing parameter a with non-constant expression", + ctx_->semantic_diagnostic(parent_, "initializing parameter a with non-constant expression", + false, p->identifier.loc); } } @@ -333,11 +340,15 @@ struct AstResolver : BaseVisitor { decl->type_id_ = ctx_->type_id(decl->type); if (decl->type_id_ == ctx_->type_id("void")) { - ctx_->semantic_error(parent_, "variable declared with void type"); + ctx_->semantic_diagnostic(parent_, "variable declared with void type", + false, + decl->identifier.loc); } if (decl->is_const_ && !decl->init) { - ctx_->semantic_error(parent_, "constant variable declaration with no initializer"); + ctx_->semantic_diagnostic(parent_, "constant variable declaration with no initializer", + false, + decl->identifier.loc); } declare(decl->identifier, decl); @@ -348,11 +359,12 @@ struct AstResolver : BaseVisitor { && decl->init->type_id_ == ctx_->type_id("int")) { // This is fine } else if (decl->type_id_ != decl->init->type_id_) { - ctx_->semantic_error(parent_, - fmt::format("initializing variable '{}' of type '{}' with value of type '{}'", + ctx_->semantic_diagnostic(parent_, + fmt::format("initializing variable '{}' of type '{}' with value of type '{}' ", decl->identifier.loc.view(), ctx_->type_name(decl->type_id_), ctx_->type_name(decl->init->type_id_)), + false, decl->identifier.loc); } } @@ -367,11 +379,12 @@ struct AstResolver : BaseVisitor { expr->rhs->accept(this); if (!ctx_->type_check_binary_op(expr->op, expr->lhs->type_id_, expr->rhs->type_id_)) { - ctx_->semantic_error(parent_, - fmt::format("invalid operands of types '{}' and '{}' to binary operator '{}'", + ctx_->semantic_diagnostic(parent_, + fmt::format("invalid operands of types '{}' and '{}' to binary operator'{}' ", ctx_->type_name(expr->lhs->type_id_), ctx_->type_name(expr->rhs->type_id_), expr->op.loc.view()), + false, expr->op.loc); return; } @@ -388,11 +401,12 @@ struct AstResolver : BaseVisitor { expr->is_const_ = expr->lhs->is_const_ && expr->rhs->is_const_; if (!ctx_->type_check_binary_op(expr->op, expr->lhs->type_id_, expr->rhs->type_id_)) { - ctx_->semantic_error(parent_, - fmt::format("invalid operands of types '{}' and '{}' to binary operator '{}'", + ctx_->semantic_diagnostic(parent_, + fmt::format("invalid operands of types '{}' and '{}' to binary operator'{}' ", ctx_->type_name(expr->lhs->type_id_), ctx_->type_name(expr->rhs->type_id_), expr->op.loc.view()), + false, expr->op.loc); return; } @@ -406,7 +420,9 @@ struct AstResolver : BaseVisitor { auto ve = dynamic_cast(expr->expr.get()); if (!ve) { // Parser already handles this case - ctx_->semantic_error(parent_, "call expressions identifier is not variable expression"); + ctx_->semantic_diagnostic(parent_, "call expressions identifier is not variable expression", + false, ve->extent()); + return; } @@ -419,8 +435,9 @@ struct AstResolver : BaseVisitor { func_decl = fd->decl_inline.get(); orig_decl = fd->decl_external; } else { - ctx_->semantic_error(parent_, + ctx_->semantic_diagnostic(parent_, fmt::format("unable to resolve identifier '{}'", ve->var.loc.view()), + false, ve->extent()); return; } @@ -434,8 +451,10 @@ struct AstResolver : BaseVisitor { } if (expr->args.size() < req || expr->args.size() > func_decl->params.size()) { - ctx_->semantic_error(parent_, - fmt::format("no matching function call '{}' expected {} parameters", expr->extent().view(), req), + ctx_->semantic_diagnostic(parent_, + fmt::format("no matching function call '{}' expected {} parameters", + expr->extent().view(), req), + false, expr->extent()); return; } @@ -450,10 +469,12 @@ struct AstResolver : BaseVisitor { && dynamic_cast(expr->args[i].get())) { // This is fine } else if (func_decl->params[i]->type_id_ != expr->args[i]->type_id_) { - ctx_->semantic_error(parent_, - fmt::format("no matching function call '{}' expected parameter type '{}'", - expr->extent().view(), + ctx_->semantic_diagnostic(parent_, + fmt::format("no matching function call '{}' expected parameter type '{}' ", + expr->extent() + .view(), ctx_->type_name(func_decl->params[i]->type_id_)), + false, expr->extent()); } } @@ -470,9 +491,12 @@ struct AstResolver : BaseVisitor { if (expr->lhs->type_id_ != expr->rhs->type_id_ && !ctx_->is_type_convertible(expr->lhs->type_id_, expr->rhs->type_id_) && !ctx_->is_type_convertible(expr->rhs->type_id_, expr->lhs->type_id_)) { - ctx_->semantic_error(parent_, - fmt::format("mismatched types in binary-expression '{}' != '{}', {}", - ctx_->type_name(expr->lhs->type_id_), ctx_->type_name(expr->rhs->type_id_), expr->extent().view()), + ctx_->semantic_diagnostic(parent_, + fmt::format( + "mismatched types in binary-expression '{}' != '{}', {} ", + ctx_->type_name(expr->lhs->type_id_), + ctx_->type_name(expr->rhs->type_id_), expr->extent().view()), + false, expr->extent()); } expr->type_id_ = ctx_->type_id("int"); @@ -483,9 +507,10 @@ struct AstResolver : BaseVisitor { expr->env = env_stack_.back(); expr->test->accept(this); if (expr->test->type_id_ != ctx_->type_id("int")) { - ctx_->semantic_error(parent_, - fmt::format("could not convert value of type '{}' to integer bool", + ctx_->semantic_diagnostic(parent_, + fmt::format("could not convert value of type '{}' to integer bool ", ctx_->type_name(expr->test->type_id_)), + false, expr->test->extent()); } @@ -493,10 +518,11 @@ struct AstResolver : BaseVisitor { expr->false_branch->accept(this); if (expr->true_branch->type_id_ != expr->false_branch->type_id_) { - ctx_->semantic_error(parent_, - fmt::format("operands of operator ?: have different types '{}' and '{}'", + ctx_->semantic_diagnostic(parent_, + fmt::format("operands of operator ?: have different types '{}' and'{}' ", ctx_->type_name(expr->true_branch->type_id_), ctx_->type_name(expr->false_branch->type_id_)), + false, expr->extent()); } @@ -520,8 +546,9 @@ struct AstResolver : BaseVisitor { auto ex_rhs = dynamic_cast(expr->rhs.get()); if (!ex_rhs) { - ctx_->semantic_error(parent_, + ctx_->semantic_diagnostic(parent_, "struct member must be a variable expression", + false, expr->dot.loc); return; } @@ -549,14 +576,18 @@ struct AstResolver : BaseVisitor { } if (!struct_decl) { - ctx_->semantic_error(parent_, - fmt::format("request for member '{}' in '{}', which is of non-struct type", - ex_rhs->var.loc.view(), struct_type), + ctx_->semantic_diagnostic(parent_, + fmt::format("request for member '{}' in '{}', which is of non - struct type ", + ex_rhs->var.loc.view(), + struct_type), + false, expr->dot.loc); } else if (!resolve_struct_member(ex_rhs, struct_decl)) { - ctx_->semantic_error(parent_, - fmt::format("request for member '{}', which is not in struct of type '{}'", - ex_rhs->var.loc.view(), struct_type), + ctx_->semantic_diagnostic(parent_, + fmt::format("request for member '{}', which is not in struct of type '{}' ", + ex_rhs->var.loc.view(), + struct_type), + false, expr->dot.loc); } @@ -601,7 +632,7 @@ struct AstResolver : BaseVisitor { expr->rhs->accept(this); if (expr->lhs->type_id_ != expr->rhs->type_id_) { - ctx_->semantic_error(parent_, "mismatched types", {}); + ctx_->semantic_diagnostic(parent_, "mismatched types", false, expr->op.loc); } expr->type_id_ = ctx_->type_id("int"); @@ -632,8 +663,9 @@ struct AstResolver : BaseVisitor { expr->type_id_ = decl->type_id_; expr->is_const_ = decl->is_const_; } else { - ctx_->semantic_error(parent_, + ctx_->semantic_diagnostic(parent_, fmt::format("unable to resolve identifier '{}'", expr->var.loc.view()), + false, expr->extent()); } } @@ -674,9 +706,10 @@ struct AstResolver : BaseVisitor { stmt->expr->accept(this); if (stmt->expr->type_id_ != ctx_->type_id("int")) { - ctx_->semantic_error(parent_, + ctx_->semantic_diagnostic(parent_, fmt::format("could not convert value of type '{}' to integer bool", ctx_->type_name(stmt->expr->type_id_)), + false, stmt->expr->extent()); } @@ -702,9 +735,10 @@ struct AstResolver : BaseVisitor { stmt->expr->accept(this); if (stmt->expr->type_id_ != ctx_->type_id("int")) { - ctx_->semantic_error(parent_, + ctx_->semantic_diagnostic(parent_, fmt::format("could not convert value of type '{}' to integer bool", ctx_->type_name(stmt->expr->type_id_)), + false, stmt->expr->extent()); } @@ -731,9 +765,10 @@ struct AstResolver : BaseVisitor { if (stmt->check) { stmt->check->accept(this); if (stmt->check->type_id_ != ctx_->type_id("int")) { - ctx_->semantic_error(parent_, + ctx_->semantic_diagnostic(parent_, fmt::format("could not convert value of type '{}' to integer bool", ctx_->type_name(stmt->check->type_id_)), + false, stmt->check->extent()); } } @@ -760,13 +795,15 @@ struct AstResolver : BaseVisitor { if (stmt->op.type == NssTokenType::CONTINUE && loop_stack_ == 0) { - ctx_->semantic_error(parent_, "continue statement not within a loop", stmt->op.loc); + ctx_->semantic_diagnostic(parent_, "continue statement not within a loop", false, stmt->op.loc); + } else if (stmt->op.type == NssTokenType::BREAK && (loop_stack_ == 0 && switch_stack_ == 0)) { - ctx_->semantic_error(parent_, "break statement not within loop or switch", stmt->op.loc); + ctx_->semantic_diagnostic(parent_, "break statement not within loop or switch", false, stmt->op.loc); + } else if (stmt->op.type == NssTokenType::RETURN && func_def_stack_ == 0) { // This shouldn't even be possible and would be a parser error - ctx_->semantic_error(parent_, "return statement not within function definition", stmt->op.loc); + ctx_->semantic_diagnostic(parent_, "return statement not within function definition", false, stmt->op.loc); } } @@ -775,7 +812,7 @@ struct AstResolver : BaseVisitor { stmt->env = env_stack_.back(); if (stmt->type.type == NssTokenType::CASE && switch_stack_ == 0) { - ctx_->semantic_error(parent_, "case statement not within switch", stmt->type.loc); + ctx_->semantic_diagnostic(parent_, "case statement not within switch", false, stmt->type.loc); } if (stmt->type.type == NssTokenType::DEFAULT) { @@ -785,10 +822,12 @@ struct AstResolver : BaseVisitor { stmt->expr->accept(this); if (stmt->expr->type_id_ != ctx_->type_id("int") && stmt->expr->type_id_ != ctx_->type_id("string")) { - ctx_->semantic_error(parent_, - fmt::format("could not convert value to integer or string")); + ctx_->semantic_diagnostic(parent_, + fmt::format("could not convert value to integer or string"), + false, stmt->expr->extent()); } else if (!stmt->expr->is_const_) { - ctx_->semantic_error(parent_, "case expression must be constant expression"); + ctx_->semantic_diagnostic(parent_, "case expression must be constant expression", + false, stmt->expr->extent()); } } @@ -804,8 +843,9 @@ struct AstResolver : BaseVisitor { // were added to NWscript, the NWN:EE team half-assed this from what // I could tell. if (stmt->target->type_id_ != ctx_->type_id("int")) { - ctx_->semantic_error(parent_, + ctx_->semantic_diagnostic(parent_, fmt::format("switch quantity not an integer"), + false, stmt->target->extent()); } @@ -824,9 +864,10 @@ struct AstResolver : BaseVisitor { stmt->check->accept(this); if (stmt->check->type_id_ != ctx_->type_id("int")) { - ctx_->semantic_error(parent_, + ctx_->semantic_diagnostic(parent_, fmt::format("could not convert value of type '{}' to integer bool", ctx_->type_name(stmt->check->type_id_)), + false, stmt->check->extent()); } diff --git a/lib/nw/script/Context.cpp b/lib/nw/script/Context.cpp index 7db3a76ef..90689619d 100644 --- a/lib/nw/script/Context.cpp +++ b/lib/nw/script/Context.cpp @@ -173,54 +173,63 @@ bool Context::is_type_convertible(size_t lhs, size_t rhs) return lhs == rhs || (lhs == float_ && rhs == int_); } -void Context::lexical_error(Nss* script, std::string_view msg, SourceLocation loc) +void Context::lexical_diagnostic(Nss* script, std::string_view msg, bool is_warning, SourceLocation loc) { - if (script) { script->increment_errors(); } - auto out = fmt::format("{}:{}:{} error: {}", script ? script->name() : "", - loc.line, loc.column, msg); - LOG_F(ERROR, "{}", out); - throw std::runtime_error(out); -} + if (script) { + if (is_warning) { + script->increment_warnings(); + } else { + script->increment_errors(); + } + } -void Context::lexical_warning(Nss* script, std::string_view msg, SourceLocation loc) -{ - if (script) { script->increment_warnings(); } - auto out = fmt::format("{}:{}:{} warning: {}", script ? script->name() : "", - loc.line, loc.column, msg); - LOG_F(WARNING, "{}", out); + auto out = fmt::format("{}:{}:{} {}: {}", script ? script->name() : "", + loc.line, loc.column, is_warning ? "warning" : "error", msg); + if (is_warning) { + LOG_F(WARNING, "{}", out); + } else { + LOG_F(ERROR, "{}", out); + throw std::runtime_error(out); + } } -void Context::parse_error(Nss* script, std::string_view msg, SourceLocation loc) +void Context::parse_diagnostic(Nss* script, std::string_view msg, bool is_warning, SourceLocation loc) { - if (script) { script->increment_errors(); } - auto out = fmt::format("{}:{}:{} error: {}", script ? script->name() : "", - loc.line, loc.column, msg); - LOG_F(ERROR, "{}", out); - throw parser_error(out); -} + if (script) { + if (is_warning) { + script->increment_warnings(); + } else { + script->increment_errors(); + } + } -void Context::parse_warning(Nss* script, std::string_view msg, SourceLocation loc) -{ - if (script) { script->increment_warnings(); } - auto out = fmt::format("{}:{}:{} warning: {}", script ? script->name() : "", - loc.line, loc.column, msg); - LOG_F(WARNING, "{}", out); + auto out = fmt::format("{}:{}:{} {}: {}", script ? script->name() : "", + loc.line, loc.column, is_warning ? "warning" : "error", msg); + if (is_warning) { + LOG_F(WARNING, "{}", out); + } else { + LOG_F(ERROR, "{}", out); + throw parser_error(out); + } } -void Context::semantic_error(Nss* script, std::string_view msg, SourceLocation loc) +void Context::semantic_diagnostic(Nss* script, std::string_view msg, bool is_warning, SourceLocation loc) { - if (script) { script->increment_errors(); } - auto out = fmt::format("{}:{}:{} error: {}", script ? script->name() : "", - loc.line, loc.column, msg); - LOG_F(ERROR, "{}", out); -} + if (script) { + if (is_warning) { + script->increment_warnings(); + } else { + script->increment_errors(); + } + } -void Context::semantic_warning(Nss* script, std::string_view msg, SourceLocation loc) -{ - if (script) { script->increment_warnings(); } - auto out = fmt::format("{}:{}:{} warning: {}", script ? script->name() : "", - loc.line, loc.column, msg); - LOG_F(WARNING, "{}", out); + auto out = fmt::format("{}:{}:{} {}: {}", script ? script->name() : "", + loc.line, loc.column, is_warning ? "warning" : "error", msg); + if (is_warning) { + LOG_F(WARNING, "{}", out); + } else { + LOG_F(ERROR, "{}", out); + } } } // nw::script diff --git a/lib/nw/script/Context.hpp b/lib/nw/script/Context.hpp index 89d164b76..1f4f1bd56 100644 --- a/lib/nw/script/Context.hpp +++ b/lib/nw/script/Context.hpp @@ -40,14 +40,9 @@ struct Context { virtual bool is_type_convertible(size_t lhs, size_t rhs); // Error/Warning Tracking - virtual void lexical_error(Nss* script, std::string_view msg, SourceLocation loc); - virtual void lexical_warning(Nss* script, std::string_view msg, SourceLocation loc); - - virtual void parse_error(Nss* script, std::string_view msg, SourceLocation loc = {}); - virtual void parse_warning(Nss* script, std::string_view msg, SourceLocation loc = {}); - - virtual void semantic_error(Nss* script, std::string_view msg, SourceLocation loc = {}); - virtual void semantic_warning(Nss* script, std::string_view msg, SourceLocation loc = {}); + virtual void lexical_diagnostic(Nss* script, std::string_view msg, bool is_warning, SourceLocation loc); + virtual void parse_diagnostic(Nss* script, std::string_view msg, bool is_warning, SourceLocation loc); + virtual void semantic_diagnostic(Nss* script, std::string_view msg, bool is_warning, SourceLocation loc); }; } // nw::script diff --git a/lib/nw/script/NssLexer.cpp b/lib/nw/script/NssLexer.cpp index 99bc48f3f..05528ae33 100644 --- a/lib/nw/script/NssLexer.cpp +++ b/lib/nw/script/NssLexer.cpp @@ -3,7 +3,6 @@ #include "../log.hpp" #include "Nss.hpp" - #include #include #include @@ -223,7 +222,8 @@ NssToken NssLexer::next() } else if (std::isdigit(get(pos_))) { t = handle_number(); } else { - ctx_->lexical_warning(parent_, fmt::format("Unrecognized character '{}'", get(pos_)), + ctx_->lexical_diagnostic(parent_, fmt::format("Unrecognized character '{}'", get(pos_)), + true, {&buffer_[start], buffer_.data() + start + 1, line_, start - last_line_pos_}); @@ -298,7 +298,7 @@ NssToken NssLexer::next() ++pos_; } if (pos_ == buffer_.size() || get(pos_) != '"') { - ctx_->lexical_error(parent_, "Unterminated quote", + ctx_->lexical_diagnostic(parent_, "Unterminated quote", false, {&buffer_[start - 1], buffer_.data() + start, line_, start - 1 - last_line_pos_}); @@ -461,7 +461,8 @@ NssToken NssLexer::next() case '*': // TIMES switch (get(pos_ + 1)) { case '/': // Uh oh - ctx_->lexical_error(parent_, "Mismatched block quote", + ctx_->lexical_diagnostic(parent_, "Mismatched block quote", + false, {&buffer_[start], &buffer_[pos_ + 1], line_, start - last_line_pos_}); @@ -477,7 +478,8 @@ NssToken NssLexer::next() case '\\': // Escape character // A couple scripts have escaped new lines (for no reason) if (get(pos_ + 1) != '\r' && get(pos_ + 1) != '\n') { - ctx_->lexical_warning(parent_, fmt::format("Unrecognized character '{}'", get(pos_)), + ctx_->lexical_diagnostic(parent_, fmt::format("Unrecognized character '{}'", get(pos_)), + true, {&buffer_[start], &buffer_[pos_ + 1], line_, start - last_line_pos_}); @@ -521,7 +523,7 @@ NssToken NssLexer::next() ++pos_; } if (!matched) { - ctx_->lexical_error(parent_, "Unterminated block quote", + ctx_->lexical_diagnostic(parent_, "Unterminated block quote", false, {&buffer_[start - 2], buffer_.data() + start, line_, start - 2 - last_line_pos_}); diff --git a/lib/nw/script/NssParser.cpp b/lib/nw/script/NssParser.cpp index 516764df6..0259450f5 100644 --- a/lib/nw/script/NssParser.cpp +++ b/lib/nw/script/NssParser.cpp @@ -70,14 +70,9 @@ bool NssParser::check_is_type() const return false; } -void NssParser::error(std::string_view msg, NssToken token) +void NssParser::diagnostic(std::string_view msg, NssToken token, bool is_warning) { - ctx_->parse_error(parent_, msg, token.loc); -} - -void NssParser::warn(std::string_view msg, NssToken token) -{ - ctx_->parse_warning(parent_, msg, token.loc); + ctx_->parse_diagnostic(parent_, msg, is_warning, token.loc); } bool NssParser::is_end() const @@ -89,7 +84,7 @@ NssToken NssParser::consume(NssTokenType type, std::string_view message) { if (check({type})) return advance(); - error(message, peek()); + diagnostic(message, peek()); throw parser_error(message); } @@ -200,7 +195,7 @@ std::unique_ptr NssParser::parse_expr_assign() return std::make_unique(std::move(expr), equals, std::move(value)); } - error("Invalid assignment target.", peek()); + diagnostic("Invalid assignment target.", peek()); } return expr; @@ -375,7 +370,7 @@ std::unique_ptr NssParser::parse_expr_postfix() if (previous().type == NssTokenType::LPAREN) { if (!dynamic_cast(expr.get())) { - error("expression cannot be used as a function", previous()); + diagnostic("expression cannot be used as a function", previous()); } auto e = std::make_unique(std::move(expr)); @@ -411,16 +406,18 @@ std::unique_ptr NssParser::parse_expr_primary() if (auto val = string::from(expr->literal.loc.view())) { expr->data = *val; } else { - ctx_->parse_error(parent_, + ctx_->parse_diagnostic(parent_, fmt::format("unable to parse integer literal '{}'", expr->literal.loc.view()), + false, expr->literal.loc); } } else if (expr->literal.type == NssTokenType::FLOAT_CONST) { if (auto val = string::from(expr->literal.loc.view())) { expr->data = *val; } else { - ctx_->parse_error(parent_, + ctx_->parse_diagnostic(parent_, fmt::format("unable to parse float literal '{}'", expr->literal.loc.view()), + false, expr->literal.loc); } } @@ -448,24 +445,27 @@ std::unique_ptr NssParser::parse_expr_primary() if (auto val = string::from(expr->x.loc.view())) { expr->data.x = *val; } else { - ctx_->parse_error(parent_, + ctx_->parse_diagnostic(parent_, fmt::format("unable to parse vector literal '{}'", expr->x.loc.view()), + false, expr->x.loc); } if (auto val = string::from(expr->y.loc.view())) { expr->data.y = *val; } else { - ctx_->parse_error(parent_, + ctx_->parse_diagnostic(parent_, fmt::format("unable to parse vector literal '{}'", expr->y.loc.view()), + false, expr->y.loc); } if (auto val = string::from(expr->z.loc.view())) { expr->data.z = *val; } else { - ctx_->parse_error(parent_, + ctx_->parse_diagnostic(parent_, fmt::format("unable to parse vector literal '{}'", expr->z.loc.view()), + false, expr->z.loc); } @@ -478,7 +478,7 @@ std::unique_ptr NssParser::parse_expr_primary() return std::make_unique(std::move(expr)); } - error("expected primary expression", peek()); + diagnostic("expected primary expression", peek()); throw parser_error("expected primary expression"); } @@ -516,7 +516,7 @@ Type NssParser::parse_type() t.struct_id = previous(); } } else { - error("Expected type specifier", peek()); + diagnostic("Expected type specifier", peek()); throw parser_error("Expected type specifier"); } @@ -779,10 +779,10 @@ Ast NssParser::parse_program() if (previous().loc.view().size() <= nw::Resref::max_size) { p.include_resrefs.push_back(std::string(previous().loc.view())); } else { - error(fmt::format("invalid include resref '{}'", previous().loc.view()), previous()); + diagnostic(fmt::format("invalid include resref '{}'", previous().loc.view()), previous()); } } else { - error("Expected string literal", peek()); + diagnostic("Expected string literal", peek()); throw parser_error("Expected string literal"); } } else if (peek().loc.view() == "define") { @@ -791,7 +791,7 @@ Ast NssParser::parse_program() if (match({NssTokenType::IDENTIFIER})) { name = std::string(previous().loc.view()); } else { - error("Expected identifier", peek()); + diagnostic("Expected identifier", peek()); throw parser_error("Expected identifier"); } value = std::string(advance().loc.view()); @@ -813,7 +813,7 @@ Ast NssParser::parse_program() std::unique_ptr NssParser::parse_decl_external() { if (match({NssTokenType::SEMICOLON})) { - warn("spurious ';'", previous()); + diagnostic("spurious ';'", previous(), true); return std::make_unique(); } @@ -851,7 +851,7 @@ std::unique_ptr NssParser::parse_decl_external() return fd; } - error("Expected function definition/declaration, struct declaration, or global variable declaration", peek()); + diagnostic("Expected function definition/declaration, struct declaration, or global variable declaration", peek()); throw parser_error("Expected function definition/declaration, struct declaration, or variable declaration"); } diff --git a/lib/nw/script/NssParser.hpp b/lib/nw/script/NssParser.hpp index 86226673c..1c0e80371 100644 --- a/lib/nw/script/NssParser.hpp +++ b/lib/nw/script/NssParser.hpp @@ -51,13 +51,9 @@ struct NssParser { /// @return Matched token NssToken consume(NssTokenType type, std::string_view error); - /// Log / Throw error - /// @param msg Error message to report - void error(std::string_view msg, NssToken token); - - /// Log warning - /// @param msg Warning message to report - void warn(std::string_view msg, NssToken token); + /// Report diagnostic + /// @param msg Message to report + void diagnostic(std::string_view msg, NssToken token, bool is_warning = false); /// Checks if at end of token stream bool is_end() const;