Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/129.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何获取正则表达式字符串的AST?_C++_Regex_Boost - Fatal编程技术网

C++ 如何获取正则表达式字符串的AST?

C++ 如何获取正则表达式字符串的AST?,c++,regex,boost,C++,Regex,Boost,如何获取正则表达式的抽象语法树(AST)(在C++中) 比如说, (XYZ)|(123) 应产生以下树木: | / \ . . / \ / \ . Z . 3 / \ / \ X Y 1 2 是否有boost::spirit语法来解析正则表达式模式?boost::regex库应该有它,但我没有找到它。有没有其他开源工具可以为我提供regex的抽象表示?boost::regex

如何获取正则表达式的抽象语法树(AST)(在C++中)

比如说,

 (XYZ)|(123)
应产生以下树木:

        |
      /   \
    .       .
   / \     / \
  .   Z   .   3    
 / \     / \   
X  Y     1 2

是否有
boost::spirit
语法来解析正则表达式模式?
boost::regex
库应该有它,但我没有找到它。有没有其他开源工具可以为我提供regex的抽象表示?

boost::regex似乎在basic_regex_parser.hpp中有一个手写的递归下降解析器。尽管感觉像是在重新发明轮子,但你自己在boost::spirit中编写语法时可能会更快,尤其是在有大量正则表达式格式的情况下。

我认为boost Xpressive必须能够“几乎”做到这一点

我看看能否(用一个小样品)确认一下

其他想法包括使用Boost Spirit和通用utree设备来“存储”AST。您必须重新生成语法(对于正则表达式语法的常见子集来说,这相对简单),因此这可能意味着需要更多的工作

进度报告1 看着Xpressive,我取得了一些进展。我用DDD强大的图形数据显示得到了一些漂亮的图片。但还不够漂亮

然后我进一步探讨了“代码”方面:Xpressive是基于Boost协议构建的。它使用PROTO来定义一个DSL,它直接在C++代码中模拟正则表达式。 PROTO从C++代码中完全生成表达式树(泛型AST,如果你愿意)(通过重载所有可能的运算符)。然后,库(在本例中为Xpressive)需要通过遍历树来定义语义,例如

  • 构建特定于域的表达式树
  • 用语义信息对其进行注释/修饰
  • 可能直接采取语义行动(例如,气和业力中的语义行动如何提升精神1)
正如您所看到的,天空是真正的极限,而且事情看起来与编译器宏非常相似,比如Boo、Nemerle、Lisp等等


可视化表达 现在,Boost原型表达树可以一般地可视化:

根据Xpressive的“Hello World”示例,我稍微扩展了Xpressive的“Hello World”示例,以显示表达式树:

#include <iostream>
#include <boost/xpressive/xpressive.hpp>
#include <boost/proto/proto.hpp>

using namespace boost::xpressive;

int main()
{
    std::string hello( "hello world!" );

    sregex rex = sregex::compile( "(\\w+) (\\w+)!" );

    // equivalent proto based expression
    rex = (s1= +_w) >> ' ' >> (s2= +_w) >> '!';
    boost::proto::display_expr( (s1= +_w) >> ' ' >> (s2= +_w) >> '!');

    smatch what;

    if( regex_match( hello, what, rex ) )
    {
        std::cout << what[0] << '\n'; // whole match
        std::cout << what[1] << '\n'; // first capture
        std::cout << what[2] << '\n'; // second capture
    }

    return 0;
}
免责声明您应该意识到,这实际上并没有显示Regex AST,而是Proto中的泛型表达式树,因此它没有特定于域(Regex)的信息。我之所以提到它,是因为这种差异可能会导致更多的工作(除非我在Xpressive的编译结构中找到一个钩子),以便它对原始问题真正有用

现在就这样 我会在那张纸条上留言,因为现在是午餐时间,我要去接孩子们,但这肯定引起了我的兴趣,所以我打算稍后再发更多的帖子


结论/进度报告1.0000001 坏消息马上就来了:这行不通

原因如下。这个免责声明在金钱上是正确的。当周末到来时,我已经仔细考虑了一些事情,并且“预测”整个事情将在我离开的地方崩溃:AST基于原型表达式树(而不是regex
matchable\u ex

经过一些代码检查后,这一事实很快得到了证实:编译后,原型表达式树不再显示。更不用说当基本正则表达式最初被指定为动态模式时(它从来没有原型表达式)

我一直希望匹配可以直接在proto-expression树上实现(使用proto-evalutation/evaluation上下文),但很快就证实了事实并非如此

因此,主要的收获是:

  • 这对于显示任何正则表达式AST都是行不通的
  • 使用上面的代码,您所能做的最好的事情就是可视化一个原型表达式,您必须直接在代码中创建它。这是一种用同样的代码手动编写AST的奇特方式
稍不严格的观察包括

  • boostproto和boostexpressive是非常有趣的库(我不介意在那里钓鱼)。我显然学到了一些关于模板元编程库的重要经验,尤其是这些库
  • 很难设计一个构建静态类型表达式树的正则表达式解析器。事实上,这在一般情况下是不可能的——它需要编译器实例化所有可能的表达式树组合到一定的深度。这显然不会扩大规模。您可以通过引入多态组合和使用多态调用来解决这个问题,但这将消除模板元编程(静态实例化类型/专门化的编译时优化)的好处
  • boostregex和boostexpressive都可能在内部支持某种Regex-AST(以支持匹配计算),但是
    • 它还没有公开/记录
    • 没有明显的显示设备
1就这点而言,即使Spirit Lex也支持他们(但不是默认情况下)

我又一次无意中问到了这个问题。我决定看看用Boost Spirit为正则表达式语法的重要子集编写解析器到底有多困难

所以,像往常一样,我从纸和笔开始,过了一段时间,我就想到了一些规则草案。绘制类似AST的时间:

namespace ast
{
    struct multiplicity 
    {
        unsigned minoccurs;
        boost::optional<unsigned> maxoccurs;
        bool greedy;

        multiplicity(unsigned minoccurs = 1, boost::optional<unsigned> maxoccurs = 1) 
            : minoccurs(minoccurs), maxoccurs(maxoccurs), greedy(true)
        { }

        bool unbounded() const { return !maxoccurs; }
        bool repeating() const { return !maxoccurs || *maxoccurs > 1; }
    };

    struct charset
    {
        bool negated;

        using range   = boost::tuple<char, char>; // from, till
        using element = boost::variant<char, range>;

        std::set<element> elements; 
        // TODO: single set for loose elements, simplify() method
    };

    struct start_of_match {};
    struct end_of_match {};
    struct any_char {};
    struct group;

    typedef boost::variant<   // unquantified expression
        start_of_match,
        end_of_match,
        any_char,
        charset,
        std::string,          // literal
        boost::recursive_wrapper<group> // sub expression
    > simple;

    struct atom               // quantified simple expression
    {
        simple       expr;
        multiplicity mult;
    };

    using sequence    = std::vector<atom>;
    using alternative = std::vector<sequence>;
    using regex       = boost::variant<atom, sequence, alternative>;

    struct group {
        alternative root;

        group() = default;
        group(alternative root) : root(std::move(root)) { }
    };
}
现在,真正的乐趣是使用AST做一些事情。因为您想可视化树,所以我考虑从AST生成点图。所以我做了:

int main()
{
    std::cout << "digraph common {\n";

    for (std::string pattern: { 
            "abc?",
            "ab+c",
            "(ab)+c",
            "[^-a\\-f-z\"\\]aaaa-]?",
            "abc|d",
            "a?",
            ".*?(a|b){,9}?",
            "(XYZ)|(123)",
        })
    {
        std::cout << "// ================= " << pattern << " ========\n";
        ast::regex tree;
        if (doParse(pattern, tree))
        {
            check_roundtrip(tree, pattern);

            regex_todigraph printer(std::cout, pattern);
            boost::apply_visitor(printer, tree);
        }
    }

    std::cout << "}\n";
}
intmain()
{

库特添加了一个小的免责声明,以防止任何误解。这是正在进行的工作。但它显示出了真正的潜在迹象,伊莫加-等待周末继续
int main()
{
    std::cout << "digraph common {\n";

    for (std::string pattern: { 
            "abc?",
            "ab+c",
            "(ab)+c",
            "[^-a\\-f-z\"\\]aaaa-]?",
            "abc|d",
            "a?",
            ".*?(a|b){,9}?",
            "(XYZ)|(123)",
        })
    {
        std::cout << "// ================= " << pattern << " ========\n";
        ast::regex tree;
        if (doParse(pattern, tree))
        {
            check_roundtrip(tree, pattern);

            regex_todigraph printer(std::cout, pattern);
            boost::apply_visitor(printer, tree);
        }
    }

    std::cout << "}\n";
}