C++ 无需跳绳就能提高精神分析能力

C++ 无需跳绳就能提高精神分析能力,c++,boost-spirit,preprocessor,C++,Boost Spirit,Preprocessor,考虑一个预处理器,它将读取原始文本(没有明显的空白或标记) 有三条规则 resolve_para_entry应解决调用中的参数。顶级文本作为字符串返回 resolve_para应解析整个参数列表,并将所有顶级参数放入字符串列表中 resolve是条目 在跟踪迭代器并获取文本部分的过程中 样本: sometext(段落)→ 字符串列表中应包含para sometext(para1,para2)→ 字符串列表中应包含para1和para2 sometext(调用(a))→ 字符串列表中应包含调

考虑一个预处理器,它将读取原始文本(没有明显的空白或标记)

有三条规则

  • resolve_para_entry
    应解决调用中的参数。顶级文本作为字符串返回

  • resolve_para
    应解析整个参数列表,并将所有顶级参数放入字符串列表中

  • resolve
    是条目

在跟踪迭代器并获取文本部分的过程中

样本:

  • sometext(段落)
    → 字符串列表中应包含
    para

  • sometext(para1,para2)
    → 字符串列表中应包含
    para1
    para2

  • sometext(调用(a))
    → 字符串列表中应包含调用(a)

  • sometext(调用(a,b))
    ← 在这里它失败了;很明显,“!lit(',')”不会将解析器带到外部

规则:

resolve_para_entry = +(  
     (iter_pos >> lit('(') >> (resolve_para_entry | eps) >> lit(')') >> iter_pos) [_val=  phoenix::bind(&appendString, _val, _1,_3)]
     | (!lit(',') >> !lit(')') >> !lit('(') >> (wide::char_ | wide::space))         [_val = phoenix::bind(&appendChar, _val, _1)]
    );

resolve_para = (lit('(') >> lit(')'))[_val = std::vector<std::wstring>()]  // empty para -> old style
    | (lit('(') >> resolve_para_entry >> *(lit(',') >> resolve_para_entry) > lit(')'))[_val = phoenix::bind(&appendStringList, _val, _1, _2)]
    | eps;
  ;

resolve = (iter_pos >> name_valid >> iter_pos >> resolve_para >> iter_pos);
resolve_para_entry=+(
(iter U pos>>lit('(')>(解析\u para\u entry | eps)>>lit(')>>iter_pos)[u val=phoenix::bind(&appendString,_val,U 1,U 3)]
|(!lit(',)>>!lit('))>>!lit('(')>>(wide::char_124;wide::space))[_val=phoenix::bind(&appendChar,_val,_1)]
);
解析_para=(lit(“(”)>>lit(“))[_val=std::vector()]//空para->old style
|(lit(“(”)>>resolve_para_entry>>>*(lit(“,”)>>resolve_para_entry)>lit(“))[\u val=phoenix::bind(&appendStringList,\u val,\u 1,\u 2)]
|每股收益;
;
解析=(iter位置>>名称有效>>iter位置>>解析段落>>iter位置);

最后看起来不是很优雅。也许有一种更好的方法可以不用skipper来解析这些东西,实际上这应该简单得多

首先,我不明白为什么船长缺席是有意义的

其次,最好使用
qi::raw[]
来公开原始输入,而不是与
iter\u pos
和笨拙的语义动作一起跳舞

在其他观察中,我看到:

  • 是用
    ~
    完成的,因此例如
    ~char(“,()”)
  • (p | eps)
    的拼写会更好
    -p
  • (lit(“(”)>>lit(“)”)
    可能只是
    “()”
    (毕竟,没有跳绳,对吧)
  • p>>*(','>>p)
    相当于
    p%,'
  • 通过以上内容,
    resolve_para
    简化为:

    resolve_para = '(' >> -(resolve_para_entry % ',') >> ')';
    
  • resolve\u para\u条目
    对我来说似乎很奇怪。似乎任何嵌套的括号都被简单地吞没了。为什么不实际解析递归语法以便检测语法错误呢


以下是我对它的看法:

定义AST 我更愿意将此作为第一步,因为它帮助我思考解析器产品:

namespace Ast {

    using ArgList = std::list<std::string>;

    struct Resolve {
        std::string name;
        ArgList arglist;
    };

    using Resolves = std::vector<Resolve>;
}
注:

  • 没有更多的语义动作
  • 没有更多的eps
  • 不再有
    iter\u pos
  • 我选择将
    arglist
    设置为非可选。如果您真的想要,请将其更改回:

    resolve    = identifier >> -arglist;
    
    但在我们的示例中,它将产生大量噪声输出

  • 当然,您的入口点(
    开始
    )会有所不同。我只是做了最简单的事情,使用Spirit存储库中的另一个方便的解析器指令(比如您已经在使用的
    iter\u pos
    ):
    seek[]

  • 暂停的原因如下:-在实际的解析器中可能不需要它

#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/repository/include/qi_seek.hpp>

namespace Ast {

    using ArgList = std::list<std::string>;

    struct Resolve {
        std::string name;
        ArgList arglist;
    };

    using Resolves = std::vector<Resolve>;
}

BOOST_FUSION_ADAPT_STRUCT(Ast::Resolve, name, arglist)

namespace qi = boost::spirit::qi;
namespace qr = boost::spirit::repository::qi;

template <typename It>
struct Parser : qi::grammar<It, Ast::Resolves()>
{
    Parser() : Parser::base_type(start) {
        using namespace qi;

        identifier = char_("a-zA-Z_") >> *char_("a-zA-Z0-9_");

        arg        = raw [ +('(' >> -arg >> ')' | +~char_(",)(")) ];
        arglist    = '(' >> -(arg % ',') >> ')';
        resolve    = identifier >> arglist;

        start      = *qr::seek[hold[resolve]];
    }
  private:
    qi::rule<It, Ast::Resolves()> start;
    qi::rule<It, Ast::Resolve()>  resolve;
    qi::rule<It, Ast::ArgList()>  arglist;
    qi::rule<It, std::string()>   arg, identifier;
};

#include <iostream>

int main() {
    using It = std::string::const_iterator;
    std::string const samples = R"--(
Samples:

sometext(para)        → expect para in the string list
sometext(para1,para2) → expect para1 and para2 in string list
sometext(call(a))     → expect call(a) in the string list
sometext(call(a,b))   ← here it fails; it seams that the "!lit(',')" wont make the parser step outside
)--";
    It f = samples.begin(), l = samples.end();

    Ast::Resolves data;
    if (parse(f, l, Parser<It>{}, data)) {
        std::cout << "Parsed " << data.size() << " resolves\n";

    } else {
        std::cout << "Parsing failed\n";
    }

    for (auto& resolve: data) {
        std::cout << " - " << resolve.name << "\n   (\n";
        for (auto& arg : resolve.arglist) {
            std::cout << "       " << arg << "\n";
        }
        std::cout << "   )\n";
    }
}
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/repository/include/qi_seek.hpp>
#include <boost/utility/string_view.hpp>

namespace Ast {

    using Source  = boost::string_view;
    using ArgList = std::list<Source>;

    struct Resolve {
        Source name;
        ArgList arglist;
    };

    using Resolves = std::vector<Resolve>;
}

BOOST_FUSION_ADAPT_STRUCT(Ast::Resolve, name, arglist)

namespace boost { namespace spirit { namespace traits {
    template <typename It>
    struct assign_to_attribute_from_iterators<boost::string_view, It, void> {
        static void call(It f, It l, boost::string_view& attr) { 
            attr = boost::string_view { f.base(), size_t(std::distance(f.base(),l.base())) };
        }
    };
} } }

namespace qi = boost::spirit::qi;
namespace qr = boost::spirit::repository::qi;

template <typename It>
struct Parser : qi::grammar<It, Ast::Resolves()>
{
    Parser() : Parser::base_type(start) {
        using namespace qi;

        identifier = raw [ char_("a-zA-Z_") >> *char_("a-zA-Z0-9_") ];

        arg        = raw [ +('(' >> -arg >> ')' | +~char_(",)(")) ];
        arglist    = '(' >> -(arg % ',') >> ')';
        resolve    = identifier >> arglist;

        start      = *qr::seek[hold[resolve]];
    }
  private:
    qi::rule<It, Ast::Resolves()> start;
    qi::rule<It, Ast::Resolve()>  resolve;
    qi::rule<It, Ast::ArgList()>  arglist;
    qi::rule<It, Ast::Source()>   arg, identifier;
};

#include <iostream>

struct Annotator {
    using Ref = boost::string_view;

    struct Manip {
        Ref fragment, context;

        friend std::ostream& operator<<(std::ostream& os, Manip const& m) {
            return os << "[" << m.fragment << " at line:" << m.line() << " col:" << m.column() << "]";
        }

        size_t line() const {
            return 1 + std::count(context.begin(), fragment.begin(), '\n');
        }
        size_t column() const {
            return 1 + (fragment.begin() - start_of_line().begin());
        }
        Ref start_of_line() const {
            return context.substr(context.substr(0, fragment.begin()-context.begin()).find_last_of('\n') + 1);
        }
    };

    Ref context;
    Manip operator()(Ref what) const { return {what, context}; }
};

int main() {
    using It = std::string::const_iterator;
    std::string const samples = R"--(Samples:

sometext(para)        → expect para in the string list
sometext(para1,para2) → expect para1 and para2 in string list
sometext(call(a))     → expect call(a) in the string list
sometext(call(a,b))   ← here it fails; it seams that the "!lit(',')" wont make the parser step outside
)--";
    It f = samples.begin(), l = samples.end();

    Ast::Resolves data;
    if (parse(f, l, Parser<It>{}, data)) {
        std::cout << "Parsed " << data.size() << " resolves\n";

    } else {
        std::cout << "Parsing failed\n";
    }

    Annotator annotate{samples};

    for (auto& resolve: data) {
        std::cout << " - " << annotate(resolve.name) << "\n   (\n";
        for (auto& arg : resolve.arglist) {
            std::cout << "       " << annotate(arg) << "\n";
        }
        std::cout << "   )\n";
    }
}
更多想法 最后的输出显示了当前语法的问题:
lit(',')
显然不应被视为具有两个参数的调用

我最近回答了关于使用参数提取(嵌套)函数调用的问题,这样做会更灵活:

  • 还是这个
奖金
额外版本,使用
string\u视图
,并显示所有提取单词的精确行/列信息

请注意,它仍然不需要任何phoenix或语义操作。相反,它只是定义了从迭代器范围分配给
boost::string_view
的必要特征

#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/repository/include/qi_seek.hpp>

namespace Ast {

    using ArgList = std::list<std::string>;

    struct Resolve {
        std::string name;
        ArgList arglist;
    };

    using Resolves = std::vector<Resolve>;
}

BOOST_FUSION_ADAPT_STRUCT(Ast::Resolve, name, arglist)

namespace qi = boost::spirit::qi;
namespace qr = boost::spirit::repository::qi;

template <typename It>
struct Parser : qi::grammar<It, Ast::Resolves()>
{
    Parser() : Parser::base_type(start) {
        using namespace qi;

        identifier = char_("a-zA-Z_") >> *char_("a-zA-Z0-9_");

        arg        = raw [ +('(' >> -arg >> ')' | +~char_(",)(")) ];
        arglist    = '(' >> -(arg % ',') >> ')';
        resolve    = identifier >> arglist;

        start      = *qr::seek[hold[resolve]];
    }
  private:
    qi::rule<It, Ast::Resolves()> start;
    qi::rule<It, Ast::Resolve()>  resolve;
    qi::rule<It, Ast::ArgList()>  arglist;
    qi::rule<It, std::string()>   arg, identifier;
};

#include <iostream>

int main() {
    using It = std::string::const_iterator;
    std::string const samples = R"--(
Samples:

sometext(para)        → expect para in the string list
sometext(para1,para2) → expect para1 and para2 in string list
sometext(call(a))     → expect call(a) in the string list
sometext(call(a,b))   ← here it fails; it seams that the "!lit(',')" wont make the parser step outside
)--";
    It f = samples.begin(), l = samples.end();

    Ast::Resolves data;
    if (parse(f, l, Parser<It>{}, data)) {
        std::cout << "Parsed " << data.size() << " resolves\n";

    } else {
        std::cout << "Parsing failed\n";
    }

    for (auto& resolve: data) {
        std::cout << " - " << resolve.name << "\n   (\n";
        for (auto& arg : resolve.arglist) {
            std::cout << "       " << arg << "\n";
        }
        std::cout << "   )\n";
    }
}
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/repository/include/qi_seek.hpp>
#include <boost/utility/string_view.hpp>

namespace Ast {

    using Source  = boost::string_view;
    using ArgList = std::list<Source>;

    struct Resolve {
        Source name;
        ArgList arglist;
    };

    using Resolves = std::vector<Resolve>;
}

BOOST_FUSION_ADAPT_STRUCT(Ast::Resolve, name, arglist)

namespace boost { namespace spirit { namespace traits {
    template <typename It>
    struct assign_to_attribute_from_iterators<boost::string_view, It, void> {
        static void call(It f, It l, boost::string_view& attr) { 
            attr = boost::string_view { f.base(), size_t(std::distance(f.base(),l.base())) };
        }
    };
} } }

namespace qi = boost::spirit::qi;
namespace qr = boost::spirit::repository::qi;

template <typename It>
struct Parser : qi::grammar<It, Ast::Resolves()>
{
    Parser() : Parser::base_type(start) {
        using namespace qi;

        identifier = raw [ char_("a-zA-Z_") >> *char_("a-zA-Z0-9_") ];

        arg        = raw [ +('(' >> -arg >> ')' | +~char_(",)(")) ];
        arglist    = '(' >> -(arg % ',') >> ')';
        resolve    = identifier >> arglist;

        start      = *qr::seek[hold[resolve]];
    }
  private:
    qi::rule<It, Ast::Resolves()> start;
    qi::rule<It, Ast::Resolve()>  resolve;
    qi::rule<It, Ast::ArgList()>  arglist;
    qi::rule<It, Ast::Source()>   arg, identifier;
};

#include <iostream>

struct Annotator {
    using Ref = boost::string_view;

    struct Manip {
        Ref fragment, context;

        friend std::ostream& operator<<(std::ostream& os, Manip const& m) {
            return os << "[" << m.fragment << " at line:" << m.line() << " col:" << m.column() << "]";
        }

        size_t line() const {
            return 1 + std::count(context.begin(), fragment.begin(), '\n');
        }
        size_t column() const {
            return 1 + (fragment.begin() - start_of_line().begin());
        }
        Ref start_of_line() const {
            return context.substr(context.substr(0, fragment.begin()-context.begin()).find_last_of('\n') + 1);
        }
    };

    Ref context;
    Manip operator()(Ref what) const { return {what, context}; }
};

int main() {
    using It = std::string::const_iterator;
    std::string const samples = R"--(Samples:

sometext(para)        → expect para in the string list
sometext(para1,para2) → expect para1 and para2 in string list
sometext(call(a))     → expect call(a) in the string list
sometext(call(a,b))   ← here it fails; it seams that the "!lit(',')" wont make the parser step outside
)--";
    It f = samples.begin(), l = samples.end();

    Ast::Resolves data;
    if (parse(f, l, Parser<It>{}, data)) {
        std::cout << "Parsed " << data.size() << " resolves\n";

    } else {
        std::cout << "Parsing failed\n";
    }

    Annotator annotate{samples};

    for (auto& resolve: data) {
        std::cout << " - " << annotate(resolve.name) << "\n   (\n";
        for (auto& arg : resolve.arglist) {
            std::cout << "       " << annotate(arg) << "\n";
        }
        std::cout << "   )\n";
    }
}


CharStyle

额外版本,使用
string\u视图
,还显示所有提取单词的精确行/列信息:您的注释是正确的,此解析器将消除所有参数。考虑一个C/C++预处理器。在定义的宏参数中写入什么并不重要,它只是按原样使用,并在文本中替换。你的其他信息需要一些时间来理解。。我会报告结果,只是简短的反馈。。代码无法运行的原因是规则resolve\u para。。在“)”之前有一个“>”而不是“>>”,所以回溯不会发生……哦,是的。我并不是真的对此感兴趣(但显然我已经解决了这个问题)。我认为治疗症状而不是疾病:)
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/repository/include/qi_seek.hpp>
#include <boost/utility/string_view.hpp>

namespace Ast {

    using Source  = boost::string_view;
    using ArgList = std::list<Source>;

    struct Resolve {
        Source name;
        ArgList arglist;
    };

    using Resolves = std::vector<Resolve>;
}

BOOST_FUSION_ADAPT_STRUCT(Ast::Resolve, name, arglist)

namespace boost { namespace spirit { namespace traits {
    template <typename It>
    struct assign_to_attribute_from_iterators<boost::string_view, It, void> {
        static void call(It f, It l, boost::string_view& attr) { 
            attr = boost::string_view { f.base(), size_t(std::distance(f.base(),l.base())) };
        }
    };
} } }

namespace qi = boost::spirit::qi;
namespace qr = boost::spirit::repository::qi;

template <typename It>
struct Parser : qi::grammar<It, Ast::Resolves()>
{
    Parser() : Parser::base_type(start) {
        using namespace qi;

        identifier = raw [ char_("a-zA-Z_") >> *char_("a-zA-Z0-9_") ];

        arg        = raw [ +('(' >> -arg >> ')' | +~char_(",)(")) ];
        arglist    = '(' >> -(arg % ',') >> ')';
        resolve    = identifier >> arglist;

        start      = *qr::seek[hold[resolve]];
    }
  private:
    qi::rule<It, Ast::Resolves()> start;
    qi::rule<It, Ast::Resolve()>  resolve;
    qi::rule<It, Ast::ArgList()>  arglist;
    qi::rule<It, Ast::Source()>   arg, identifier;
};

#include <iostream>

struct Annotator {
    using Ref = boost::string_view;

    struct Manip {
        Ref fragment, context;

        friend std::ostream& operator<<(std::ostream& os, Manip const& m) {
            return os << "[" << m.fragment << " at line:" << m.line() << " col:" << m.column() << "]";
        }

        size_t line() const {
            return 1 + std::count(context.begin(), fragment.begin(), '\n');
        }
        size_t column() const {
            return 1 + (fragment.begin() - start_of_line().begin());
        }
        Ref start_of_line() const {
            return context.substr(context.substr(0, fragment.begin()-context.begin()).find_last_of('\n') + 1);
        }
    };

    Ref context;
    Manip operator()(Ref what) const { return {what, context}; }
};

int main() {
    using It = std::string::const_iterator;
    std::string const samples = R"--(Samples:

sometext(para)        → expect para in the string list
sometext(para1,para2) → expect para1 and para2 in string list
sometext(call(a))     → expect call(a) in the string list
sometext(call(a,b))   ← here it fails; it seams that the "!lit(',')" wont make the parser step outside
)--";
    It f = samples.begin(), l = samples.end();

    Ast::Resolves data;
    if (parse(f, l, Parser<It>{}, data)) {
        std::cout << "Parsed " << data.size() << " resolves\n";

    } else {
        std::cout << "Parsing failed\n";
    }

    Annotator annotate{samples};

    for (auto& resolve: data) {
        std::cout << " - " << annotate(resolve.name) << "\n   (\n";
        for (auto& arg : resolve.arglist) {
            std::cout << "       " << annotate(arg) << "\n";
        }
        std::cout << "   )\n";
    }
}
Parsed 6 resolves
 - [sometext at line:3 col:1]
   (
       [para at line:3 col:10]
   )
 - [sometext at line:4 col:1]
   (
       [para1 at line:4 col:10]
       [para2 at line:4 col:16]
   )
 - [sometext at line:5 col:1]
   (
       [call(a) at line:5 col:10]
   )
 - [call at line:5 col:34]
   (
       [a at line:5 col:39]
   )
 - [call at line:6 col:10]
   (
       [a at line:6 col:15]
       [b at line:6 col:17]
   )
 - [lit at line:6 col:62]
   (
       [' at line:6 col:66]
       [' at line:6 col:68]
   )