rsql-parser/src/main.cpp

199 lines
6.9 KiB
C++

#include "matador/rsql/binary_condition_node.hpp"
#include "matador/rsql/collection_condition_node.hpp"
#include "matador/rsql/lexer.hpp"
#include "matador/rsql/logical_node.hpp"
#include "matador/rsql/node_visitor.hpp"
#include "matador/rsql/parser.hpp"
#include <iostream>
// ---------------- Evaluator Visitor (prints AST) ----------------
class Evaluator final : public matador::rsql::node_visitor {
public:
void visit(const matador::rsql::binary_condition_node& node) override {
std::cout << "Condition: " << node.field() << " " << to_string(node.operand()) << " " << node.value() << "\n";
}
void visit( const matador::rsql::collection_condition_node& node ) override {
std::cout << "Condition: " << node.field() << " " << to_string(node.operand()) << " ";
std::cout << "(";
for (size_t i = 0; i < node.values().size(); ++i) {
std::cout << node.values()[i];
if (i + 1 < node.values().size()) std::cout << ",";
}
std::cout << ")\n";
}
void visit(const matador::rsql::logical_node& node) override {
std::cout << "Logical (" << (node.operand() == matador::rsql::logical_operator::And ? "AND" : "OR") << ")\n";
for (const auto& child : node.children()) {
child->accept(*this);
}
}
private:
static std::string to_string(const matador::rsql::binary_operator op) {
static std::map<matador::rsql::binary_operator, std::string> bin_to_string {
{ matador::rsql::binary_operator::Equals, "==" },
{ matador::rsql::binary_operator::NotEquals, "!=" },
{ matador::rsql::binary_operator::GreaterThan, ">" },
{ matador::rsql::binary_operator::GreaterThanOrEqual, ">=" },
{ matador::rsql::binary_operator::LessThan, "<" },
{ matador::rsql::binary_operator::LessThanOrEqual, "<=" },
};
return bin_to_string.at(op);
}
static std::string to_string(const matador::rsql::collection_operator op) {
static std::map<matador::rsql::collection_operator, std::string> col_to_string {
{ matador::rsql::collection_operator::In, "=IN=" },
{ matador::rsql::collection_operator::Out, "=OUT=" }
};
return col_to_string.at(op);
}
};
// ---------------- SQLBuilderVisitor ----------------
class SQLBuilderVisitor final : public matador::rsql::node_visitor {
public:
std::string result;
void visit(const matador::rsql::binary_condition_node& node) override {
result += "(" + field(node.field()) + " " + binary_op_string(node.operand(), node.value()) + ")";
}
void visit( const matador::rsql::collection_condition_node& node ) override {
result += "(" + field(node.field()) + " " + collection_op_string(node.operand(), node.values()) + ")";
}
void visit(const matador::rsql::logical_node& node) override {
// Build children SQL and join
std::vector<std::string> parts;
for (const auto& child : node.children()) {
SQLBuilderVisitor sub;
child->accept(sub);
parts.push_back(sub.result);
}
const std::string operand = (node.operand() == matador::rsql::logical_operator::And ? " AND " : " OR ");
result += "(";
for (size_t i = 0; i < parts.size(); ++i) {
result += parts[i];
if (i + 1 < parts.size()) {
result += operand;
}
}
result += ")";
}
private:
static std::string escape_sql(const std::string& s) {
std::string out;
out.reserve(s.size() + 2);
for (const char c : s) {
if (c == '\'') out.push_back('\''); // double single-quote
out.push_back(c);
}
return out;
}
static bool is_number(const std::string& s) {
if (s.empty()) {
return false;
}
// allow optional leading '-' and digits, optional decimal point
size_t i = 0;
if (s[0] == '-') i = 1;
bool seenDigit = false;
bool seenDot = false;
for (; i < s.size(); ++i) {
if (std::isdigit(static_cast<unsigned char>(s[i]))) seenDigit = true;
else if (s[i] == '.' && !seenDot) seenDot = true;
else return false;
}
return seenDigit;
}
static std::string quote(const std::string& str) {
if (is_number(str)) {
return str;
}
return std::string("'") + escape_sql(str) + std::string("'");
}
static std::string field(const std::string& f) {
// Todo: add quoting rules
return f;
}
static std::string binary_op_string(const matador::rsql::binary_operator op, const std::string& val) {
using namespace matador::rsql;
switch (op) {
case binary_operator::Equals:
return "= " + quote(val);
case binary_operator::NotEquals:
return "<> " + quote(val);
case binary_operator::GreaterThan:
return "> " + quote(val);
case binary_operator::GreaterThanOrEqual:
return ">= " + quote(val);
case binary_operator::LessThan:
return "< " + quote(val);
case binary_operator::LessThanOrEqual:
return "<= " + quote(val);
default:
return "??";
}
}
static std::string collection_op_string(const matador::rsql::collection_operator op, const std::vector<std::string>& list) {
if (list.empty()) {
return (op == matador::rsql::collection_operator::Out ? "NOT IN (NULL)" : "IN (NULL)");
}
std::string joined;
joined += "(";
for (size_t i = 0; i < list.size(); ++i) {
joined += quote(list[i]);
if (i + 1 < list.size()) joined += ", ";
}
joined += ")";
if (op == matador::rsql::collection_operator::Out) {
return "IN " + joined;
}
return "NOT IN " + joined;
}
};
// ---------------- Main / usage ----------------
int main(const int argc, char** argv) {
using namespace matador::rsql;
std::string input;
if (argc >= 2) {
input = argv[1];
} else {
// example default
input = "(status=in=(OPEN,CLOSED);priority==HIGH),category=out=(internal,test)";
std::cout << "No query provided, using default example:\n" << input << "\n\n";
}
try {
const auto tokens = lexer::tokenize(input);
parser p(tokens);
const auto ast = p.parse();
std::cout << "=== AST (Evaluator) ===\n";
Evaluator ev;
ast->accept(ev);
std::cout << "\n=== SQL WHERE clause ===\n";
SQLBuilderVisitor sql;
ast->accept(sql);
std::cout << sql.result << "\n";
} catch (const std::exception& ex) {
std::cerr << "Error: " << ex.what() << "\n";
return 2;
}
return 0;
}