【问题标题】:How to use boost::spirit to modify a string like regex does?如何使用 boost::spirit 来修改像正则表达式一样的字符串?
【发布时间】:2020-03-12 18:40:25
【问题描述】:

我正在为我的程序编写一个小的领域特定语言,使用JUCE::JavascriptEngine 作为脚本引擎。这需要一个字符串作为输入,然后对其进行解析,但我需要对字符串进行一些预处理以使其从我的 DSL 适应 JavaScript。预处理主要包括将一些术语包装在函数内部,并将对象名称放在函数前面。所以,例如,我想做这样的事情:

取一些特殊的字符串输入"~/1/2"...

将其包装在一个函数中:"find("~/1/2")"...

然后将对象附加到它:"someObject.find("~/1/2")"(对象名称必须是变量)。

我一直在为此使用正则表达式(现在我有两个问题......)。正则表达式变得复杂且不可读,并且缺少很多特殊情况。由于我正在做的是语法,我想我会从正则表达式升级到一个合适的解析器(现在我有三个问题......)。经过大量研究,我选择了 Boost.Spirit。我一直在阅读文档,但这并没有让我朝着正确的方向前进。有人可以建议我如何使用这个库以我正在寻找的方式操作字符串吗?鉴于我只是试图操作一个字符串并且对存储解析的数据不感兴趣,我是否需要使用karma 输出,或者我可以在解析期间使用qix3 输出字符串过程?

如果我在这里走错了路,请随时重新引导我。

【问题讨论】:

    标签: boost boost-spirit


    【解决方案1】:

    这似乎太宽泛了,无法回答。

    您正在做的是解析输入,并将其转换为其他内容。您没有做的是查找/替换(否则您可以使用正则表达式)。

    当然你可以做正则表达式做的事,但我不确定它能给你带来什么:

    template <typename It, typename Out>
    Out preprocess(It f, It l, Out out) {
        namespace qi = boost::spirit::qi;
        using boost::spirit::repository::qi::seek;
    
        auto passthrough = [&out](boost::iterator_range<It> ignored, auto&&...) {
            for (auto ch : ignored) *out++ = ch;
        };
        auto transform = [&out](std::string const& literal, auto&&...) {
            for (auto ch : "someObject.find(\"~"s) *out++ = ch;
            for (auto ch : literal) *out++ = ch;
            for (auto ch : "\")"s) *out++ = ch;
        };
    
        auto pattern = qi::copy("\"~" >> (*~qi::char_('"')) >> '"');
        qi::rule<It> ignore = qi::raw[+(!pattern >> qi::char_)] [passthrough];
        qi::parse(f, l, -qi::as_string[pattern][transform] % ignore);
        return out;
    }
    

    这种编写方式的好处是它可以与任何源迭代器一起使用:

    for (std::string const input : {
        R"(function foo(a, b) { var path = "~/1/2"; })",
    })
    {
        std::cout << "Input: " << input << "\n";
    
        std::string result;
        preprocess(begin(input), end(input), back_inserter(result));
    
        std::cout << "Result: " << result << "\n";
    }
    
    std::cout << "\n -- Or directly transformed stdin to stdout:\n";
    preprocess(
        boost::spirit::istream_iterator(std::cin >> std::noskipws), {},
        std::ostreambuf_iterator<char>(std::cout));
    

    看到Live On Coliru,打印输出:

    Input: function foo(a, b) { var path = "~/1/2"; }
    Result: function foo(a, b) { var path = someObject.find("~/1/2"); }
    
     -- Or directly transformed stdin to stdout:
    function bar(c, d) { var path = someObject.find("~/1/42"); }
    

    但这非常有限,因为如果这些东西是 cmets 或 multiline strings 等的一部分,它甚至不会做正确的事情。

    因此,您可能需要一个专门的库,该库知道如何解析 javascript 并使用它来进行转换,例如(谷歌搜索时的第一个命中 tooling library preprocess javascript transform):https://clojurescript.org/reference/javascript-library-preprocessing

    【讨论】:

      猜你喜欢
      • 2014-01-29
      • 1970-01-01
      • 2018-09-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多