199 lines
6.9 KiB
C++
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;
|
|
}
|