Skip to content

Commit

Permalink
feat: revise syntax error messages.
Browse files Browse the repository at this point in the history
  • Loading branch information
ashigeru committed Jul 5, 2024
1 parent cf5f026 commit 6368659
Show file tree
Hide file tree
Showing 15 changed files with 476 additions and 52 deletions.
11 changes: 11 additions & 0 deletions include/mizugaki/parser/sql_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ class sql_parser {
/// @brief the result type.
using result_type = sql_parser_result;

/// @brief default number of next token candidates to display on error.
static constexpr std::size_t default_max_expected_candidates = 5;

/**
* @brief sets the max number of next token candidates to display on error.
* @param count the max number of candidates, or 0 to disable to display
* @see default_max_expected_candidates
*/
sql_parser max_expected_candidates(std::size_t count) noexcept;

/**
* @brief sets the debug level.
* @param level the debug level
Expand All @@ -47,6 +57,7 @@ class sql_parser {
[[nodiscard]] result_type operator()(::takatori::util::maybe_shared_ptr<document_type const> document) const;

private:
std::size_t max_expected_candidates_ { default_max_expected_candidates };
int debug_ {};
};

Expand Down
69 changes: 69 additions & 0 deletions include/mizugaki/parser/sql_parser_code.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#pragma once

#include <ostream>
#include <string>
#include <string_view>

namespace mizugaki::parser {

/**
* @brief represents diagnostic code of SQL analyzer.
*/
enum class sql_parser_code {
/// @brief unknown diagnostic.
unknown = 0,

/// @brief diagnostics generated by parser system.
system,

/// @brief violates syntax rule restrictions.
syntax_error,

/// @brief the target feature is not supported.
unsupported_feature,

/// @brief the document includes unrecognized characters.
invalid_character,

/// @brief the token format is not appropriate for syntax.
invalid_token,

/// @brief the occurred token is not appropriate for syntax rules.
unexpected_token,

/// @brief the document includes unexpected EOF.
unexpected_eof,
};

/**
* @brief returns string representation of the value.
* @param value the target value
* @return the corresponded string representation
*/
inline constexpr std::string_view to_string_view(sql_parser_code value) noexcept {
using namespace std::string_view_literals;
using kind = sql_parser_code;
switch (value) {
case kind::unknown: return "unknown"sv;
case kind::system: return "system"sv;
case kind::syntax_error: return "syntax_error"sv;
case kind::unsupported_feature: return "unsupported_feature"sv;
case kind::invalid_character: return "invalid_character"sv;
case kind::invalid_token: return "invalid_token"sv;
case kind::unexpected_token: return "unexpected_token"sv;
case kind::unexpected_eof: return "unexpected_eof"sv;
}
std::abort();
}

/**
* @brief appends string representation of the given value.
* @param out the target output
* @param value the target value
* @return the output
*/
inline std::ostream& operator<<(std::ostream& out, sql_parser_code value) {
return out << to_string_view(value);
}

} // namespace mizugaki::parser
64 changes: 35 additions & 29 deletions include/mizugaki/parser/sql_parser_diagnostic.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,18 @@

#include <mizugaki/ast/node_region.h>

#include "sql_parser_code.h"

namespace mizugaki::parser {

/**
* @brief diagnostic information of parsing SQL.
*/
class sql_parser_diagnostic {
public:
/// @brief the diagnostic code type.
using code_type = sql_parser_code;

/// @brief the diagnostic message type.
using message_type = std::string;

Expand All @@ -31,75 +36,76 @@ class sql_parser_diagnostic {

/**
* @brief creates a new instance
* @param code the diagnostic code
* @param message the diagnostic message
* @param document the source document
* @param region the diagnostic region on the document
*/
explicit sql_parser_diagnostic(
code_type code,
message_type message,
::takatori::util::maybe_shared_ptr<document_type const> document = {},
region_type region = {}) noexcept :
message_(std::move(message)),
document_ { std::move(document) },
region_ { region }
{}
region_type region = {}) noexcept;

/**
* @brief returns whether or not this diagnostic information is valid.
* @return true if this is valid
* @return false otherwise
*/
[[nodiscard]] bool is_valid() const noexcept {
return !message_.empty();
}
[[nodiscard]] bool is_valid() const noexcept;

/// @copydoc is_valid()
[[nodiscard]] explicit operator bool() const noexcept {
return is_valid();
}
[[nodiscard]] explicit operator bool() const noexcept;

/**
* @brief returns the diagnostic code.
* @return the diagnostic code
* @return sql_parser_code::unknown if this is an empty diagnostic
*/
[[nodiscard]] code_type& code() noexcept;

/// @copydoc code()
[[nodiscard]] code_type const& code() const noexcept;

/**
* @brief returns the diagnostic message.
* @return the diagnostic message
* @return empty if this is an empty diagnostic
*/
[[nodiscard]] message_type& message() noexcept {
return message_;
}
[[nodiscard]] message_type& message() noexcept;

/// @copydoc message()
[[nodiscard]] message_type const& message() const noexcept {
return message_;
}
[[nodiscard]] message_type const& message() const noexcept;

/**
* @brief returns the source document.
* @return the source document
* @return empty if it is not sure
*/
[[nodiscard]] ::takatori::util::maybe_shared_ptr<document_type const>& document() noexcept {
return document_;
}
[[nodiscard]] ::takatori::util::maybe_shared_ptr<document_type const>& document() noexcept;

/// @copydoc document()
[[nodiscard]] ::takatori::util::maybe_shared_ptr<document_type const> const& document() const noexcept {
return document_;
}
[[nodiscard]] ::takatori::util::maybe_shared_ptr<document_type const> const& document() const noexcept;

/**
* @brief returns the region of this diagnostic.
* @return the diagnostic region
*/
[[nodiscard]] constexpr region_type& region() noexcept {
return region_;
}
[[nodiscard]] region_type& region() noexcept;

/// @copydoc region()
[[nodiscard]] constexpr region_type const& region() const noexcept {
return region_;
}
[[nodiscard]] region_type const& region() const noexcept;

/**
* @brief returns the contents of the diagnostic region.
* @return the contents
* @return empty string if the region is empty (may represent EOF)
* @attention the returned contents will be disabled after document() was disposed
*/
[[nodiscard]] std::string_view contents() const;

private:
code_type code_ { code_type::unknown };
message_type message_ {};
::takatori::util::maybe_shared_ptr<document_type const> document_ {};
region_type region_ {};
Expand Down
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ add_library(mizugaki

# SQL parser
mizugaki/parser/sql_parser.cpp
mizugaki/parser/sql_parser_diagnostic.cpp
mizugaki/parser/sql_parser_result.cpp
mizugaki/parser/sql_scanner.cpp
mizugaki/parser/sql_driver.cpp
Expand Down
1 change: 1 addition & 0 deletions src/mizugaki/parser/details/sequence_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ inline bool set(sql_driver& driver, sequence_options& options, sequence_option_f
if (options.saw.contains(field.kind)) {
using ::takatori::util::string_builder;
driver.error(
sql_parser_code::syntax_error,
field.region,
string_builder {}
<< "duplicate declaration of " << to_string_view(field.kind)
Expand Down
31 changes: 30 additions & 1 deletion src/mizugaki/parser/sql_driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,12 @@ void sql_driver::success(std::vector<node_ptr<ast::statement::statement>> statem
document_);
}

void sql_driver::error(sql_driver::location_type location, sql_parser_result::message_type message) {
void sql_driver::error(
diagnostic_code_type code,
location_type location,
result_type::message_type message) {
result_ = sql_parser_diagnostic {
code,
std::move(message),
document_,
location,
Expand All @@ -64,6 +68,10 @@ void sql_driver::add_comment(location_type location) {
comments_.emplace_back(location);
}

std::size_t &sql_driver::max_expected_candidates() noexcept {
return max_expected_candidates_;
}

std::vector<sql_driver::location_type>& sql_driver::comments() noexcept {
return comments_;
}
Expand Down Expand Up @@ -121,6 +129,27 @@ std::size_t sql_driver::to_size(ast::common::chars const& str) { // NOLINT: non-
return result;
}

std::string_view sql_driver::image(location_type location) {
if (location.size() == 0) {
return {};
}
return document_->contents(location.first(), location.size());
}

bool sql_driver::check_regular_identifier(const ast::common::chars& str) {
if (str.size() >= 2) {
return str[0] != '_' || str[1] != '_';
}
return true;
}

bool sql_driver::check_delimited_identifier(const ast::common::chars &str) {
if (str.size() >= 3) {
return str[1] != '_' || str[2] != '_';
}
return true;
}

ast::common::chars sql_driver::parse_regular_identifier(ast::common::chars str) { // NOLINT
return str;
}
Expand Down
19 changes: 18 additions & 1 deletion src/mizugaki/parser/sql_driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include <mizugaki/ast/statement/statement.h>

#include <mizugaki/parser/sql_parser_result.h>
#include <mizugaki/parser/sql_parser_code.h>


namespace mizugaki::parser {

Expand All @@ -18,6 +20,8 @@ class sql_driver {
using document_type = ::takatori::document::document;
using result_type = sql_parser_result;

using diagnostic_code_type = sql_parser_code;

using location_type = ast::node_region;

template<class T>
Expand All @@ -33,10 +37,15 @@ class sql_driver {

void success(std::vector<node_ptr<ast::statement::statement>> statements);

void error(location_type location, result_type::message_type message);
void error(
diagnostic_code_type code,
location_type location,
result_type::message_type message);

void add_comment(location_type location);

[[nodiscard]] std::size_t& max_expected_candidates() noexcept;

[[nodiscard]] std::vector<location_type>& comments() noexcept;

[[nodiscard]] std::vector<location_type> const& comments() const noexcept;
Expand Down Expand Up @@ -69,6 +78,12 @@ class sql_driver {

[[nodiscard]] std::size_t to_size(ast::common::chars const& str);

[[nodiscard]] bool check_regular_identifier(ast::common::chars const& str);

[[nodiscard]] bool check_delimited_identifier(ast::common::chars const& str);

[[nodiscard]] std::string_view image(location_type location);

[[nodiscard]] ast::common::chars parse_regular_identifier(ast::common::chars str);

[[nodiscard]] ast::common::chars parse_delimited_identifier(ast::common::chars const& str);
Expand All @@ -95,6 +110,8 @@ class sql_driver {
::takatori::util::maybe_shared_ptr<document_type const> document_;
std::vector<location_type> comments_;
result_type result_ {};

std::size_t max_expected_candidates_ {};
};

} // namespace sandbox
7 changes: 7 additions & 0 deletions src/mizugaki/parser/sql_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ namespace mizugaki::parser {

using ::takatori::document::basic_document;

sql_parser sql_parser::max_expected_candidates(std::size_t count) noexcept {
max_expected_candidates_ = count;
return *this;
}

sql_parser& sql_parser::set_debug(int level) noexcept {
debug_ = level;
return *this;
Expand All @@ -31,6 +36,8 @@ sql_parser::result_type sql_parser::operator()(takatori::util::maybe_shared_ptr<
sql_scanner scanner { input };

sql_driver driver { std::move(document) };
driver.max_expected_candidates() = max_expected_candidates_;

sql_parser_generated parser { scanner, driver };

#if YYDEBUG
Expand Down
Loading

0 comments on commit 6368659

Please sign in to comment.