我创建了一个数据库引擎,可以在其中创建和修改表,并将它们添加到数据库中。为了解析SQL查询,我已经使用EBNF形式实现了Boost.Spirit库。我已经正确设置了解析器,它可以成功解析每个规则。
我的问题是我现在不知道如何整合两者。Boost.Spirit解析器仅验证输入是否正确,但是我需要它来实际 执行 某些操作。我查看了语义动作,但是它们似乎无法满足我的需求。
例如,如果我有一个查询,例如: new_table <- SELECT (id < 5) old_table;
new_table <- SELECT (id < 5) old_table;
我希望它使用规则来验证输入,然后调用该函数 Table Database::Select(Table t , Condition c){ ... } 并将令牌作为参数传递。
Table Database::Select(Table t , Condition c){ ... }
我该如何整合解析器?
注意:由于您的问题并未显示您的问题,因此我选择在此处出于示范目的发明一个示例语法。使用此处推荐的方法,在解析后编写一个函数以执行查询就不难了。
我真的建议建一个解析树。
属性传播规则在Spirit中非常灵活。每个解析器的文档都正确地记录了默认的公开属性类型
例如-qi::char_会导致,boost::optional<char>并且qi::double_ | qi::int_会导致boost::variant<double, int>。
qi::char_
boost::optional<char>
qi::double_ | qi::int_
boost::variant<double, int>
您可能希望将自己发明的AST数据类型中的已解析元素累积起来,例如:
struct SelectStatement { std::vector<std::string> columns, fromtables; std::string whereclause; // TODO model as a vector<WhereCondition> :) friend std::ostream& operator<<(std::ostream& os, SelectStatement const& ss) { return os << "SELECT [" << ss.columns.size() << " columns] from [" << ss.fromtables.size() << " tables]\nWHERE " + ss.whereclause; } };
您可以通过将结构调整为Fusion序列来使其适应Spirits属性传播机制:
BOOST_FUSION_ADAPT_STRUCT(SelectStatement, (std::vector<std::string>, columns) (std::vector<std::string>, fromtables) (std::string, whereclause) )
现在,您可以将以下规则解析为该类型:
sqlident = lexeme [ alpha >> *alnum ]; // table or column name columns = no_case [ "select" ] >> (sqlident % ','); tables = no_case [ "from" ] >> (sqlident % ','); start = columns >> tables >> no_case [ "where" ] >> lexeme [ +(char_ - ';') ] >> ';';
您可以在此处 实时 查看此 代码 :http : //liveworkspace.org/code/0b525234dbce22cbd8becd69f84065c1
完整的演示代码:
// #define BOOST_SPIRIT_DEBUG #include <boost/fusion/adapted.hpp> #include <boost/spirit/include/qi.hpp> namespace qi = boost::spirit::qi; struct SelectStatement { std::vector<std::string> columns, fromtables; std::string whereclause; // TODO model as a vector<WhereCondition> :) friend std::ostream& operator<<(std::ostream& os, SelectStatement const& ss) { return os << "SELECT [" << ss.columns.size() << " columns] from [" << ss.fromtables.size() << " tables]\nWHERE " + ss.whereclause; } }; BOOST_FUSION_ADAPT_STRUCT(SelectStatement, (std::vector<std::string>, columns) (std::vector<std::string>, fromtables) (std::string, whereclause) ) template <typename It, typename Skipper = qi::space_type> struct parser : qi::grammar<It, SelectStatement(), Skipper> { parser() : parser::base_type(start) { using namespace qi; sqlident = lexeme [ alpha >> *alnum ]; // table or column name columns = no_case [ "select" ] >> (sqlident % ','); tables = no_case [ "from" ] >> (sqlident % ','); start = columns >> tables >> no_case [ "where" ] >> lexeme [ +(char_ - ';') ] >> ';'; BOOST_SPIRIT_DEBUG_NODE(start); BOOST_SPIRIT_DEBUG_NODE(sqlident); BOOST_SPIRIT_DEBUG_NODE(columns); BOOST_SPIRIT_DEBUG_NODE(tables); } private: qi::rule<It, std::string() , Skipper> sqlident; qi::rule<It, std::vector<std::string>(), Skipper> columns , tables; qi::rule<It, SelectStatement() , Skipper> start; }; template <typename C, typename Skipper> bool doParse(const C& input, const Skipper& skipper) { auto f(std::begin(input)), l(std::end(input)); parser<decltype(f), Skipper> p; SelectStatement query; try { bool ok = qi::phrase_parse(f,l,p,skipper,query); if (ok) { std::cout << "parse success\n"; std::cout << "query: " << query << "\n"; } else std::cerr << "parse failed: '" << std::string(f,l) << "'\n"; if (f!=l) std::cerr << "trailing unparsed: '" << std::string(f,l) << "'\n"; return ok; } catch(const qi::expectation_failure<decltype(f)>& e) { std::string frag(e.first, e.last); std::cerr << e.what() << "'" << frag << "'\n"; } return false; } int main() { const std::string input = "select id, name, price from books, authors where books.author_id = authors.id;"; bool ok = doParse(input, qi::space); return ok? 0 : 255; }
将打印输出:
parse success query: SELECT [3 columns] from [2 tables] WHERE books.author_id = authors.id