Skip to content

Commit

Permalink
feat(clp-s): Add boilerplate for SQL parsing. (#504)
Browse files Browse the repository at this point in the history
Co-authored-by: Lin Zhihao <[email protected]>
  • Loading branch information
gibber9809 and LinZhihao-723 authored Jan 21, 2025
1 parent 09bab5a commit 0c00a94
Show file tree
Hide file tree
Showing 11 changed files with 193 additions and 30 deletions.
2 changes: 2 additions & 0 deletions components/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,7 @@ set(SOURCE_FILES_unitTest
tests/test-Stopwatch.cpp
tests/test-StreamingCompression.cpp
tests/test-string_utils.cpp
tests/test-sql.cpp
tests/test-TimestampPattern.cpp
tests/test-utf8_utils.cpp
tests/test-Utils.cpp
Expand All @@ -627,6 +628,7 @@ target_link_libraries(unitTest
${MONGOCXX_TARGET}
simdjson
spdlog::spdlog
sql
OpenSSL::Crypto
${sqlite_LIBRARY_DEPENDENCIES}
${STD_FS_LIBS}
Expand Down
1 change: 1 addition & 0 deletions components/core/src/clp_s/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
add_subdirectory(search/kql)
add_subdirectory(search/sql)

set(
CLP_SOURCES
Expand Down
36 changes: 36 additions & 0 deletions components/core/src/clp_s/search/antlr_common/ErrorListener.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#ifndef CLP_S_SEARCH_ANTLRCOMMON_ERRORLISTENER_HPP
#define CLP_S_SEARCH_ANTLRCOMMON_ERRORLISTENER_HPP

#include <cstddef>
#include <exception>
#include <string>
#include <string_view>

#include <antlr4-runtime.h>

namespace clp_s::search::antlr_common {
class ErrorListener : public antlr4::BaseErrorListener {
public:
auto syntaxError(
[[maybe_unused]] antlr4::Recognizer* recognizer,
[[maybe_unused]] antlr4::Token* offending_symbol,
[[maybe_unused]] size_t line,
[[maybe_unused]] size_t char_position_in_line,
std::string const& msg,
[[maybe_unused]] std::exception_ptr e
) -> void override {
m_error = true;
m_error_message = msg;
}

[[nodiscard]] auto error() const -> bool { return m_error; }

[[nodiscard]] auto message() const -> std::string_view { return m_error_message; }

private:
bool m_error{false};
std::string m_error_message;
};
} // namespace clp_s::search::antlr_common

#endif // CLP_S_SEARCH_ANTLRCOMMON_ERRORLISTENER_HPP
2 changes: 1 addition & 1 deletion components/core/src/clp_s/search/kql/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ ANTLR_TARGET(
add_library(
kql
../../Utils.hpp
../antlr_common/ErrorListener.hpp
../AndExpr.hpp
../BooleanLiteral.hpp
../ColumnDescriptor.hpp
Expand All @@ -26,4 +27,3 @@ add_library(
target_compile_features(kql PRIVATE cxx_std_20)
target_include_directories(kql PRIVATE ${ANTLR_KqlParser_OUTPUT_DIR})
target_link_libraries(kql PRIVATE antlr4_static Boost::filesystem)

36 changes: 7 additions & 29 deletions components/core/src/clp_s/search/kql/kql.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,9 @@
#include <antlr4-runtime.h>
#include <spdlog/spdlog.h>

#include "KqlBaseVisitor.h"
#include "KqlLexer.h"
#include "KqlParser.h"
// If redlining may want to add ${workspaceFolder}/build/**
// to include path for vscode C/C++ utils

#include "../../Utils.hpp"
#include "../AndExpr.hpp"
#include "../antlr_common/ErrorListener.hpp"
#include "../BooleanLiteral.hpp"
#include "../ColumnDescriptor.hpp"
#include "../DateLiteral.hpp"
Expand All @@ -23,34 +18,16 @@
#include "../NullLiteral.hpp"
#include "../OrExpr.hpp"
#include "../StringLiteral.hpp"
#include "KqlBaseVisitor.h"
#include "KqlLexer.h"
#include "KqlParser.h"

using namespace antlr4;
using namespace kql;
using clp_s::search::antlr_common::ErrorListener;

namespace clp_s::search::kql {
class ErrorListener : public BaseErrorListener {
public:
void syntaxError(
Recognizer* recognizer,
Token* offending_symbol,
size_t line,
size_t char_position_in_line,
std::string const& msg,
std::exception_ptr e
) override {
m_error = true;
m_error_message = msg;
}

bool error() const { return m_error; }

std::string const& message() const { return m_error_message; }

private:
bool m_error{false};
std::string m_error_message;
};

namespace {
class ParseTreeVisitor : public KqlBaseVisitor {
private:
static void
Expand Down Expand Up @@ -236,6 +213,7 @@ class ParseTreeVisitor : public KqlBaseVisitor {
return base;
}
};
} // namespace

std::shared_ptr<Expression> parse_kql_expression(std::istream& in) {
ErrorListener lexer_error_listener;
Expand Down
29 changes: 29 additions & 0 deletions components/core/src/clp_s/search/sql/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
ANTLR_TARGET(
SqlParser
Sql.g4
LEXER PARSER VISITOR
PACKAGE sql
)

add_library(
sql
../../Utils.hpp
../antlr_common/ErrorListener.hpp
../AndExpr.hpp
../BooleanLiteral.hpp
../ColumnDescriptor.hpp
../DateLiteral.hpp
../EmptyExpr.hpp
../Expression.hpp
../FilterExpr.hpp
../Integral.hpp
../NullLiteral.hpp
../OrExpr.hpp
../StringLiteral.hpp
${ANTLR_SqlParser_CXX_OUTPUTS}
sql.cpp
sql.hpp
)
target_compile_features(sql PRIVATE cxx_std_20)
target_include_directories(sql PRIVATE ${ANTLR_SqlParser_OUTPUT_DIR})
target_link_libraries(sql PRIVATE antlr4_static Boost::filesystem)
6 changes: 6 additions & 0 deletions components/core/src/clp_s/search/sql/Sql.g4
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Boilerplate for work in progress SQL grammar.
grammar Sql;

start: EOF ;

SPACE: [ \t\r\n] -> skip ;
63 changes: 63 additions & 0 deletions components/core/src/clp_s/search/sql/sql.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#include <any>
#include <iostream>
#include <memory>

#include <antlr4-runtime.h>
#include <spdlog/spdlog.h>

#include "../antlr_common/ErrorListener.hpp"
#include "../EmptyExpr.hpp"
#include "../Expression.hpp"
#include "SqlBaseVisitor.h"
#include "SqlLexer.h"
#include "SqlParser.h"

using antlr4::ANTLRInputStream;
using antlr4::CommonTokenStream;
using clp_s::search::antlr_common::ErrorListener;
using sql::SqlBaseVisitor;
using sql::SqlLexer;
using sql::SqlParser;

namespace clp_s::search::sql {
namespace {
class ParseTreeVisitor : public SqlBaseVisitor {
public:
[[nodiscard]] auto visitStart([[maybe_unused]] SqlParser::StartContext* ctx)
-> std::any override {
return EmptyExpr::create();
}
};
} // namespace

auto parse_sql_expression(std::istream& in) -> std::shared_ptr<Expression> {
ErrorListener lexer_error_listener;
ErrorListener parser_error_listener;

ANTLRInputStream input{in};
SqlLexer lexer{&input};
lexer.removeErrorListeners();
lexer.addErrorListener(&lexer_error_listener);
CommonTokenStream tokens{&lexer};
SqlParser parser(&tokens);
parser.removeErrorListeners();
parser.addErrorListener(&parser_error_listener);
SqlParser::StartContext* tree{parser.start()};

if (lexer_error_listener.error()) {
SPDLOG_ERROR("Lexer error: {}", lexer_error_listener.message());
return nullptr;
}
if (parser_error_listener.error()) {
SPDLOG_ERROR("Parser error: {}", parser_error_listener.message());
return nullptr;
}

ParseTreeVisitor visitor;
try {
return std::any_cast<std::shared_ptr<Expression>>(visitor.visitStart(tree));
} catch (std::exception const& e) {
return nullptr;
}
}
} // namespace clp_s::search::sql
18 changes: 18 additions & 0 deletions components/core/src/clp_s/search/sql/sql.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#ifndef CLP_S_SEARCH_SQL_SQL_HPP
#define CLP_S_SEARCH_SQL_SQL_HPP

#include <istream>
#include <memory>

#include "../Expression.hpp"

namespace clp_s::search::sql {
/**
* Parses an SQL expression from the given stream to generate a search AST.
* @param in Input stream containing an SQL expression followed by EOF
* @return a search AST on success, nullptr otherwise
*/
[[nodiscard]] auto parse_sql_expression(std::istream& in) -> std::shared_ptr<Expression>;
} // namespace clp_s::search::sql

#endif // CLP_S_SEARCH_SQL_SQL_HPP
7 changes: 7 additions & 0 deletions components/core/tests/LogSuppressor.hpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
#ifndef TESTS_LOGSUPPRESSOR_HPP
#define TESTS_LOGSUPPRESSOR_HPP

#include <spdlog/spdlog.h>

/**
* A class that suppresses logs so long as it's instantiated.
*/
Expand All @@ -19,3 +24,5 @@ class LogSuppressor {
private:
spdlog::level::level_enum m_previous_logging_level;
};

#endif // TESTS_LOGSUPPRESSOR_HPP
23 changes: 23 additions & 0 deletions components/core/tests/test-sql.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#include <memory>
#include <sstream>

#include <Catch2/single_include/catch2/catch.hpp>

#include "../src/clp_s/search/EmptyExpr.hpp"
#include "../src/clp_s/search/sql/sql.hpp"
#include "LogSuppressor.hpp"

using clp_s::search::EmptyExpr;
using clp_s::search::sql::parse_sql_expression;
using std::stringstream;

TEST_CASE("Test parsing SQL", "[SQL]") {
// Suppress logging
LogSuppressor const suppressor;

SECTION("Stub accepts empty string") {
stringstream empty_string{""};
auto filter = std::dynamic_pointer_cast<EmptyExpr>(parse_sql_expression(empty_string));
REQUIRE((nullptr != filter));
}
}

0 comments on commit 0c00a94

Please sign in to comment.