如何使用Boost.X pressive正确解析胡子? [英] How to parse mustache with Boost.Xpressive correctly?

查看:239
本文介绍了如何使用Boost.X pressive正确解析胡子?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图写的小胡子解析器凭借着出色的 Boost.X pressive 从辉煌的埃里克Niebler 的。但因为这是我的第一个解析器我不熟悉的编译器作家的正常的方针和行话,感觉有点试用后&放几天丢失;错误。所以,我来到这里,希望有人能告诉我,我的n00bish方式的愚蠢;)

这是HTML code与我要提取的胡子模板( HTTP://mustache.github .IO / ):
现在<大胆>是{{#TIME}} gugus {{时代周报}}奥德nicht {{/时间}}< I>作为所有好男人< / I>来为它们的LT的{007}援助; /&大胆GT; {{国家}}。结果:{{#RES1}}罪刑< B> EST< / B>蒙迪{{/ RES1}}

我有以下问题,我还不能单独解决:


  • 我写的解析器不会打印出任何东西,但也不会发出在编译时警告。我设法之前把它打印出来的胡子code的部分,但从来没有这一切正常。

  • 我不知道我怎么能遍历所有code找到所有出现但也与 SMATCH什么访问他们像; 变量。该文档只显示了如何找到第一次出现的是什么或如何输出所有出现的迭代。

    • 其实我需要的是两者的结合。因为一旦事情是找到我需要质疑的标签名称和标签(其中什么会提供,但迭代器将不允许)之间的内容 - 并采取相应行动。我想我可以用行动,但怎么样?

    • 我觉得应该可以做标记的发现和标记之间的内容一举,对吧?或者,我需要解析器2倍的 - 如果又如何


  • 什么情况下可以解析开幕式和闭幕式括号像我一样,因为有总是的2个支架?或者我应该做的顺序或使用重复< 2,2 GT;?('{')

  • 我还是觉得有点不确定的情况下,保持() by_ref()是必要的,当最好不要使用它们。

  • 我找不到迭代器 sregex_token_iterator CUR的第四个参数的其他选项(str.begin(),str.end(),HTML,-1); 这里的 1 ,其输出以外的所有匹配的标签。

  • 是我的解析器字符串找到正确嵌套的标签胡子?

的#include<升压/ X pressive / X pressive_static.hpp>
#包括LT&;升压/ X pressive / match_results.hpp>
的typedef的std ::字符串::为const_iterator它;
使用空间boost :: X pressive;性病::字符串str =现在<大胆>是{{#TIME}} gugus {{时代周报}}奥德nicht {{/时间}}< I>作为所有好男人< /我>到前来{007}的&LT的援助; /大胆> {{国家}}结果:{{#RES1}}罪刑< b> EST< / b>蒙迪{{/ RES1}};
//解析器设置---------------------------------------------- ----------
mark_tag MTAG(1),cond_mtag(2),USER_STR(3);
sregex括号={{
                  >>保持(MTAG =重复序列。1,20>(_W))
                  >> }}
                  ;sregex ​​cond_brackets ={{#
                   >>保持(cond_mtag =重复序列。1,20>(_W))
                   >> }}
                   >> *(
                       保持(USER_STR = +(* _s>> +&alnum GT;> * _s))|
                       by_ref(括号)|
                       by_ref(cond_brackets)
                   )
                   >> {{/
                   >> cond_mtag
                   >> }}
                   ;
sregex ​​MEX pression = *(by_ref(cond_brackets)| by_ref(括号内));//循环+醒目的结果--------------------------------------
SMATCH what2;
性病::法院LT&;< \\ nregex_search:\\ n<< STR<<的'\\ n';
它strBegin = str.begin(),strEnd = str.end();
INT IC = 0;做
{
    如果(!regex_search(strBegin,strEnd,what2,MEX pression))
    {
        性病::法院LT&;< \\ T>>!这辈子...退出爆发后<< IC标签;< 循环(多个)。 <<的std :: ENDL;
        打破;
    }
    其他
    {
        性病::法院LT&;< **回路数:<< IC标签;<的'\\ n';
        性病::法院LT&;< \\ twhat2 [0]&下;&下; what2 [0]&下;&下;的'\\ n'; //全场比赛
        性病::法院LT&;< \\ twhat2 [MTAG]<< what2 [MTAG<<的'\\ n';
        性病::法院LT&;< \\ twhat2 [cond_mtag]<< what2 [cond_mtag<<的'\\ n';
        性病::法院LT&;< \\ twhat2 [USER_STR]<< what2 [USER_STR<<的'\\ n';
        //显示嵌套结果
        的std :: for_each的(
            what2.nested_results()。开始()
            what2.nested_results()。结束()
            output_nested_results()//< - 距离E.Nieblers文档相同的功能
        );        strBegin = what2 [0]。第二;
    }
    ++ IC;
}
而(IC芯片1!6 || strBegin = str.end());


解决方案

下面是@sehe,现在工作在GCC> 4.8,并在Linux和Windows CLANG正确完整code。再次非常感谢队友的这真棒帮助,即使这意味着我可以埋葬点¯xpressive:D

以下行已经变更或补充道:

  //  - 
#定义BOOST_RESULT_OF_USE_DECLTYPE
// -
结构to_string_f {
模板< typename的T>
标准::字符串运算符()(T&const的放大器; 5)const的{返回v.to_string(); }};
// -
段%={{>>感>>参考文献[SECTION_ID = to_string(_1)] GT;> }}
                >>序列//内容
                > ({{>>('/'>>语义[亮起(SECTION_ID))>>}});
// -
PHX ::功能< to_string_f> to_string;

//#定义BOOST_SPIRIT_DEBUG
#定义BOOST_RESULT_OF_USE_DECLTYPE
#定义BOOST_SPIRIT_USE_PHOENIX_V3
#包括LT&;升压/融合/调整/ struct.hpp>
#包括LT&;升压/精神/有/ qi.hpp>
#包括LT&;升压/精神/有/ phoenix.hpp>
#包括LT&;升压/实用/ string_ref.hpp>
的#include<功能>
#包括LT&;地图和GT;命名空间胡子{    //任何原子直接引用源迭代器效率
    使用boost :: string_ref;
    模板< typename的种类>结构原子{
        string_ref值;        原子(){}
        原子(string_ref常量和放大器;值):值(值){}        朋友的std :: ostream的&放大器;运营商的LT;≤(的std :: ostream的和放大器; OS,const的原子和放大器; 5){返回OS<< typeid的(V)。名称()<< [<< v.value<< ]; }
    };    //原子
    使用逐字=原子<结构verbatim_tag取代;
    采用可变=原子<结构variable_tag取代;
    使用部分=原子<结构partial_tag取代;    //模板元件(任何原子或部分)
    结构部分;    使用melement =的boost ::变体LT;
            逐字,
            变量,
            局部的,// TODO注释和设置分隔符
            提高:: recursive_wrapper<节>
        取代;    //模板:元素序列
    使用序列=的std ::矢量< melement取代;    //部分:递归定义为包含模板序列
    结构部分{
        布尔感; //正或负
        string_ref控制;
        顺序的内容;
    };
}BOOST_FUSION_ADAPT_STRUCT(胡子::节(布尔,有义)(升压:: string_ref,控制)(小胡子::序列,内容))命名空间补气=的boost ::精神::补气;
命名空间PHX =提振::凤;结构to_string_f {
    模板< typename的T>
    标准::字符串运算符()(T&const的放大器; 5)const的{返回v.to_string(); }
};模板< typename的迭代器>
    结构mustache_grammar:补气::语法<迭代器,胡子::序列()>
{
    mustache_grammar():mustache_grammar :: base_type(序列)
    {
        使用命名空间补气;
        静态常量_a_type SECTION_ID = {}; //本地
        使用boost ::凤凰::建设;
        使用boost ::凤凰::开始;
        使用boost ::凤凰::大小;        序列= *元;
        元素=
                    !(亮({{)>>'/')>> //部分高端结束当前序列
                    (部分|部分|变量|逐字);        参考=原[语义[+(图 - }})]
                        [_val =构建<提高:: string_ref>(安培; *开始(_1),大小(_1));        部分=齐亮::({{)>> >中与GT;>参考>> }};        感=('#'> attr指示(真))
                     | ('^'> attr指示(假));        段%={{>>感>>参考文献[SECTION_ID = to_string(_1)] GT;> }}
                    >>序列//内容
                    > ({{>>('/'>>语义[亮起(SECTION_ID))>>}});        变量={{>>参考>> }};        逐字=原[语义[+(char_ - {{)]
                        [_val =构建<提高:: string_ref>(安培; *开始(_1),大小(_1));        BOOST_SPIRIT_DEBUG_NODES(
                (序列)(元)(部分)(可变)(部分)(逐字)
                (参考)(有义)
            )
    }
  私人的:
    PHX ::功能< to_string_f> to_string;
    齐::规则<迭代器,胡子::序列()>序列;
    齐::规则<迭代器,胡子:: melement()>元件;
    齐::规则<迭代器,胡子::部分()>部分;
    齐::规则<迭代器,胡子::节(),齐::当地人<标准::字符串> >部分;
    齐::规则<迭代器,布尔()>感; //阳性或阴性
    齐::规则<迭代器,胡子::变量()>变量;
    齐::规则<迭代器,胡子::逐字()>逐字;
    齐::规则<迭代器,提振:: string_ref()>参考;
};命名空间倾销{
    结构自卸车:提振:: static_visitor<的std :: ostream的和放大器;>
    {
        的std :: ostream的&放大器;运算符()(的std :: ostream的和放大器; OS,小胡子::序列常量和放大器; 5)const的{
            为(自动&安培;元素:V)
                提高:: apply_visitor的(性病::绑定(风挡()的std :: REF(OS)的std ::占位符:: _ 1),元素);
            返回操作系统;
        }
        的std :: ostream的&放大器;运算符()(的std :: ostream的和放大器; OS,小胡子::逐字常量和放大器; 5)const的{
            返回OS<< v.value;
        }
        的std :: ostream的&放大器;运算符()(的std :: ostream的和放大器; OS,小胡子::变量和常量放大器; 5)const的{
            返回OS<< {{<< v.value<< }};
        }
        的std :: ostream的&放大器;运算符()(的std :: ostream的和放大器; OS,小胡子::部分常量和放大器; 5)const的{
            返回OS<< {{><< v.value<< }};
        }
        的std :: ostream的&放大器;运算符()(的std :: ostream的和放大器; OS,小胡子::部分常量和放大器; 5)const的{
            OS<< {{<< (v.sense'#':'^'?)LT;< v.control<< }};
            (*此)(OS,v.content);
            返回OS<< {{/<< v.control<< }};
        }
    };
}命名空间ContextExpander {    结构无{};    使用值=的boost :: make_recursive_variant<
        零,
        双,
        标准::字符串,
        性病::地图<的std ::字符串,提振:: recursive_variant_>中
        的std ::矢量<提高:: recursive_variant_>
    > ::类型;    用快译通=的std ::地图<的std ::字符串值取代;
    使用数组=的std ::矢量<值取代;    静态内联的std :: ostream的&放大器;运营商的LT;≤(的std :: ostream的和放大器; OS,无常量和放大器;){返回OS<< #零#; }
    静态内联的std :: ostream的&放大器;运营商的LT;≤(的std :: ostream的和放大器; OS,快译通常量和放大器; 5){返回OS<< #DICT(&所述;&下; v.size()&所述;&下;)#; }
    静态内联的std :: ostream的&放大器;运营商的LT;≤(的std :: ostream的和放大器; OS,常量数组和放大器; 5){返回OS<< #ARRAY(&所述;&下; v.size()&所述;&下;)#; }    结构扩展:提高:: static_visitor<的std :: ostream的和放大器;>
    {
        的std :: ostream的&放大器;运算符()(的std :: ostream的和放大器; OS,const的价值和放大器; CTX,小胡子::序列常量和放大器; 5)const的{
            为(自动&安培;元素:V)
                提高:: apply_visitor的(性病::绑定(扩展器()的std :: REF(OS)的std ::占位符:: _ 1,性病::占位符:: _ 2),CTX,元素);
            返回操作系统;
        }        模板< typename的了Ctx>
        的std :: ostream的&放大器;运算符()(的std :: ostream的和放大器; OS,常量了Ctx和放大器; / * *忽略/,小胡子::逐字常量和放大器; 5)const的{
            返回OS<< v.value;
        }        的std :: ostream的&放大器;运算符()(的std :: ostream的和放大器; OS,快译通常量和放大器; CTX,小胡子::变量和常量放大器; 5)const的{
            自动它= ctx.find(v.value.to_string());
            如果(它!= ctx.end())
                OS<< IT->第二个;
            返回操作系统;
        }        模板< typename的了Ctx>
        的std :: ostream的&放大器;运算符()(的std :: ostream的和放大器; OS,常量了Ctx和放大器;,胡子::变量和常量放大器;)常量{
            返回操作系统;
        }        的std :: ostream的&放大器;运算符()(的std :: ostream的和放大器; OS,快译通常量和放大器; CTX,小胡子::部分常量和放大器; 5)const的{
            自动它= ctx.find(v.value.to_string());
            如果(它!= ctx.end())
            {
                静态常量mustache_grammar<的std ::字符串::为const_iterator>磷;                汽车常量和放大器;子模板=提振::获得<标准::字符串>(IT->第二个);
                标准::字符串::为const_iterator第一= subtemplate.begin(),最后= subtemplate.end();                胡子::序列dynamic_template;
                如果(气::解析(第一个,最后,P,dynamic_template))
                    返回(*本)(OS,值{} CTX,dynamic_template);
            }
            返回OS<< #错误#;
        }        的std :: ostream的&放大器;运算符()(的std :: ostream的和放大器; OS,快译通常量和放大器; CTX,小胡子::部分常量和放大器; 5)const的{
            自动它= ctx.find(v.control.to_string());
            如果(它!= ctx.end())
                提高:: apply_visitor的(性病::绑定(do_section()的std :: REF(OS)的std ::占位符:: _ 1,性病:: CREF(V)),它 - >第二个);
            否则,如果(!v.sense)
                (*此)(OS,值{/ *无* /},v.content);            返回操作系统;
        }        模板< typename的了Ctx,typename的T>
        的std :: ostream的&放大器;运算符()(的std :: ostream的和放大器; OS,常量了Ctx和放大器; / * * CTX /,T&const的放大器; / * *元/)const的{
            返回OS<< [TBI:<< __ preTTY_FUNCTION__<< ];
        }      私人的:
        结构do_section:升压:: static_visitor<> {
            void运算符()(STD :: ostream的和放大器; OS,常量数组和放大器; CTX,小胡子::部分常量和放大器; 5)const的{
                为(自动&安培;项目:CTX)
                    扩展器()(OS,项目,v.content);
            }
            模板< typename的了Ctx>
            void运算符()(STD :: ostream的和放大器; OS,常量了Ctx和放大器; CTX,小胡子::部分常量和放大器; 5)const的{
                如果(v.sense ==感实性(CTX))
                    扩展器()(OS,价值(CTX),v.content);
            }
          私人的:
            静态布尔感实性(无){返回false; }
            静态布尔感实性(双D){返回0 = D; }
            模板< typename的T>静态布尔感实性(T&const的放大器; 5){返回v.empty()!; }
        };
    };}INT myMain()
{
    性病::法院LT&;<的std :: unitbuf;
    性病::字符串输入=< UL> {{#时间}} \\ n \\ T<李> {{>部分}}< /李> {{/时间}}< / UL> \\ N
        < I>作为所有好男人< /我>到前来的{007}援助
        他们< /大胆> {{国家}}结果:{{^的Res2}}(无){{/的Res2}} {{#的Res2}} {{的Res2}} {{/的Res2}}
        ;
    //解析器设置---------------------------------------------- ----------
    的typedef的std ::字符串::为const_iterator它;
    静态常量mustache_grammar<它>磷;    它首先= input.begin(),最后= input.end();    尝试{
        胡子::序列parsed_template;
        如果(气::解析(第一个,最后,P,parsed_template))
        {
            性病::法院LT&;< 解析成功\\ n;
        }其他
        {
            性病::法院LT&;< 解析失败\\ n;
        }        如果(第一!=上)
        {
            性病::法院LT&;< Remaing未解析输入:'<<标准::字符串(第一,最后)LT;< '\\ n;
        }        性病::法院LT&;< 输入:<<输入<< \\ n;
        性病::法院LT&;< 转储;
        倾倒::自卸车()(STD ::法院,parsed_template)LT;< \\ n;        性病::法院LT&;< 评估;        {
            使用命名空间ContextExpander;
            扩展引擎;            值常量CTX = {快译通
                {时间,阵{
                    快译通{{部分,gugus {{时代周报}}(又名< U>的{{title}}< / U>)},{称号,正午},{时代周报, 12:00},
                    快译通{{部分,gugus {{时代周报}}(又名< U>的{{title}}< / U>)},{称号,黄昏},{时代周报, 19:30}},
                    快译通{{部分,gugus< U>的{{title}}< / U>(预期在大约{{时代周报}})},{称号,黎明},{时代周报 06:00}},
                }},
                {国家,ESP},
                {RES3,未使用}
            };            发动机(标准::法院,CTX,parsed_template);
        }
    }赶上(气:: expectation_failure<它>常量与评估)
    {
        性病::法院LT&;< 意外的:'<<标准::字符串(e.first,e.last)LT;< '\\ n;
    }
}

I have tried to write a mustache parser with the excellent Boost.XPressive from the brilliant Eric Niebler. But since this is my first parser I am not familiar with the "normal" approach and lingo of compiler writers and feel a bit lost after a few days of trial&error. So I come here and hope someone can tell me the foolishness of my n00bish ways ;)

This is the HTML code with the mustache templates that I want to extract (http://mustache.github.io/): Now <bold>is the {{#time}}gugus {{zeit}} oder nicht{{/time}} <i>for all good men</i> to come to the {007} aid of their</bold> {{country}}. Result: {{#Res1}}Nullum <b>est</b> mundi{{/Res1}}

I have the following problems that I couldn't yet solve alone:

  • The parser I wrote doesn't print out anything but also doesn't issue a warning at compile-time. I managed before to have it print out parts of the mustache code but never all of it correctly.
  • I don't know how I can loop through all the code to find all occurrences but then also access them like with the smatch what; variable. The doc only shows how to find the first occurrence with "what" or how to output all the occurrences with the "iterator".
    • Actually I need a combination of both. Because once something is found I need to question the tags name and the content between the tags (which "what" would offer but the "iterator" won't allow) - and act accordingly. I guess I could use "actions" but how?
    • I think that it should be possible to do the tag finding and "content between tags" in one swoop, right? Or do I need to parser 2 times for that - and if so how?
  • Is it okay to parse the opening and closing brackets like I did, since there are always 2 brackets? Or should I do it in sequence or use repeat<2,2>('{')?
  • I still feel a bit unsure about the cases where keep() and by_ref() are necessary and when better not to use them.
  • I couldn't find the other options of the 4th parameter of the iterator sregex_token_iterator cur( str.begin(), str.end(), html, -1 ); here -1 which outputs all except the matching tags.
  • Is my parser string correctly finding nested mustache tags?

#include <boost/xpressive/xpressive_static.hpp>
#include <boost/xpressive/match_results.hpp>
typedef std::string::const_iterator It;
using namespace boost::xpressive;

std::string str = "Now <bold>is the {{#time}}gugus {{zeit}} oder nicht{{/time}} <i>for all good men</i> to come to the {007} aid of their</bold> {{country}}. Result: {{#Res1}}Nullum <b>est</b> mundi{{/Res1}}";
// Parser setup --------------------------------------------------------
mark_tag mtag (1), cond_mtag (2), user_str (3);
sregex brackets = "{{"
                  >> keep ( mtag = repeat<1, 20> (_w) )
                  >> "}}"
                  ;

sregex cond_brackets = "{{#"
                   >> keep (cond_mtag = repeat<1, 20> (_w) )
                   >> "}}"
                   >> * (
                       keep (user_str = + (*_s >> +alnum >> *_s) ) |
                       by_ref (brackets) |
                       by_ref (cond_brackets)
                   )
                   >> "{{/"
                   >> cond_mtag
                   >> "}}"
                   ;
sregex mexpression = *( by_ref (cond_brackets) | by_ref (brackets) );

// Looping + catching the results --------------------------------------
smatch what2;
std::cout << "\nregex_search:\n" << str << '\n';
It strBegin = str.begin(), strEnd = str.end();
int ic = 0;

do
{
    if ( !regex_search ( strBegin, strEnd, what2, mexpression ) )
    {
        std::cout << "\t>> Breakout of this life...! Exit after " << ic << " loop(s)." << std::endl;
        break;
    }
    else
    {
        std::cout << "**Loop Nr: " << ic << '\n';
        std::cout << "\twhat2[0] "         << what2[0]      << '\n'; // whole match
        std::cout << "\twhat2[mtag] "      << what2[mtag]   << '\n';
        std::cout << "\twhat2[cond_mtag] " << what2[cond_mtag]   << '\n';
        std::cout << "\twhat2[user_str] "  << what2[user_str]    << '\n';
        // display the nested results
        std::for_each (
            what2.nested_results().begin(),
            what2.nested_results().end(),
            output_nested_results() // <--identical function from E.Nieblers documentation
        );

        strBegin = what2[0].second;
    }
    ++ic;
}
while (ic < 6 || strBegin != str.end() );

解决方案

Here is the correct full code from @sehe that now works under GCC >4.8 and CLANG under Linux and Windows. Again many thanks mate for this awesome help, even though this means that I can bury XPressive :D

The following lines have changed or been added:

// --
#define BOOST_RESULT_OF_USE_DECLTYPE
// --
struct to_string_f {
template <typename T>
std::string operator()(T const& v) const { return v.to_string(); }};
// --
section     %= "{{" >> sense >> reference [ section_id = to_string(_1) ] >> "}}"
                >> sequence // contents
                > ("{{" >> ('/' >> lexeme [ lit(section_id) ]) >> "}}");
// --
phx::function<to_string_f> to_string;

//#define BOOST_SPIRIT_DEBUG
#define BOOST_RESULT_OF_USE_DECLTYPE
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/utility/string_ref.hpp>
#include <functional>
#include <map>

namespace mustache {

    // any atom refers directly to source iterators for efficiency
    using boost::string_ref;
    template <typename Kind> struct atom {
        string_ref value;

        atom() { }
        atom(string_ref const& value) : value(value) { }

        friend std::ostream& operator<<(std::ostream& os, atom const& v) { return os << typeid(v).name() << "[" << v.value << "]"; }
    };

    // the atoms
    using verbatim = atom<struct verbatim_tag>;
    using variable = atom<struct variable_tag>;
    using partial  = atom<struct partial_tag>;

    // the template elements (any atom or a section)
    struct section;

    using melement = boost::variant<
            verbatim,
            variable,
            partial, // TODO comments and set-separators
            boost::recursive_wrapper<section>
        >;

    // the template: sequences of elements
    using sequence = std::vector<melement>;

    // section: recursively define to contain a template sequence
    struct section {
        bool       sense; // positive or negative
        string_ref control;
        sequence   content;
    };
}

BOOST_FUSION_ADAPT_STRUCT(mustache::section, (bool, sense)(boost::string_ref, control)(mustache::sequence, content))

namespace qi = boost::spirit::qi;
namespace phx= boost::phoenix;

struct to_string_f {
    template <typename T>
    std::string operator()(T const& v) const { return v.to_string(); }
};

template <typename Iterator>
    struct mustache_grammar : qi::grammar<Iterator, mustache::sequence()>
{
    mustache_grammar() : mustache_grammar::base_type(sequence)
    {
        using namespace qi;
        static const _a_type section_id = {}; // local
        using boost::phoenix::construct;
        using boost::phoenix::begin;
        using boost::phoenix::size;

        sequence     = *element;
        element      = 
                    !(lit("{{") >> '/') >> // section-end ends the current sequence
                    (partial | section | variable | verbatim);

        reference    = raw [ lexeme [ +(graph - "}}") ] ]
                        [ _val = construct<boost::string_ref>(&*begin(_1), size(_1)) ];

        partial      = qi::lit("{{") >> "> " >> reference >> "}}";

        sense        = ('#' > attr(true))
                     | ('^' > attr(false));

        section     %= "{{" >> sense >> reference [ section_id = to_string(_1) ] >> "}}"
                    >> sequence // contents
                    > ("{{" >> ('/' >> lexeme [ lit(section_id) ]) >> "}}");

        variable     = "{{" >> reference >> "}}";

        verbatim     = raw [ lexeme [ +(char_ - "{{") ] ]
                        [ _val = construct<boost::string_ref>(&*begin(_1), size(_1)) ];

        BOOST_SPIRIT_DEBUG_NODES(
                (sequence)(element)(partial)(variable)(section)(verbatim)
                (reference)(sense)
            )
    }
  private:
    phx::function<to_string_f> to_string;
    qi::rule<Iterator, mustache::sequence()> sequence;
    qi::rule<Iterator, mustache::melement()> element;
    qi::rule<Iterator, mustache::partial()>  partial;
    qi::rule<Iterator, mustache::section(), qi::locals<std::string> >  section;
    qi::rule<Iterator, bool()>                sense;                  // postive  or negative
    qi::rule<Iterator, mustache::variable()> variable;
    qi::rule<Iterator, mustache::verbatim()> verbatim;
    qi::rule<Iterator, boost::string_ref()>   reference;
};

namespace Dumping {
    struct dumper : boost::static_visitor<std::ostream&>
    {
        std::ostream& operator()(std::ostream& os, mustache::sequence const& v) const {
            for(auto& element : v)
                boost::apply_visitor(std::bind(dumper(), std::ref(os), std::placeholders::_1), element);
            return os;
        }
        std::ostream& operator()(std::ostream& os, mustache::verbatim const& v) const {
            return os << v.value;
        }
        std::ostream& operator()(std::ostream& os, mustache::variable const& v) const {
            return os << "{{" << v.value << "}}";
        }
        std::ostream& operator()(std::ostream& os, mustache::partial const& v) const {
            return os << "{{> " << v.value << "}}";
        }
        std::ostream& operator()(std::ostream& os, mustache::section const& v) const {
            os << "{{" << (v.sense?'#':'^') << v.control << "}}";
            (*this)(os, v.content);
            return os << "{{/" << v.control << "}}";
        }
    };
}

namespace ContextExpander {

    struct Nil { };

    using Value = boost::make_recursive_variant<
        Nil,
        double,
        std::string,
        std::map<std::string, boost::recursive_variant_>,
        std::vector<boost::recursive_variant_>
    >::type;

    using Dict  = std::map<std::string, Value>;
    using Array = std::vector<Value>;

    static inline std::ostream& operator<<(std::ostream& os, Nil   const&)   { return os << "#NIL#"; }
    static inline std::ostream& operator<<(std::ostream& os, Dict  const& v) { return os << "#DICT("  << v.size() << ")#"; }
    static inline std::ostream& operator<<(std::ostream& os, Array const& v) { return os << "#ARRAY(" << v.size() << ")#"; }

    struct expander : boost::static_visitor<std::ostream&>
    {
        std::ostream& operator()(std::ostream& os, Value const& ctx, mustache::sequence const& v) const {
            for(auto& element : v)
                boost::apply_visitor(std::bind(expander(), std::ref(os), std::placeholders::_1, std::placeholders::_2), ctx, element);
            return os;
        }

        template <typename Ctx>
        std::ostream& operator()(std::ostream& os, Ctx const&/*ignored*/, mustache::verbatim const& v) const {
            return os << v.value;
        }

        std::ostream& operator()(std::ostream& os, Dict const& ctx, mustache::variable const& v) const {
            auto it = ctx.find(v.value.to_string());
            if (it != ctx.end())
                os << it->second;
            return os;
        }

        template <typename Ctx>
        std::ostream& operator()(std::ostream& os, Ctx const&, mustache::variable const&) const {
            return os;
        }

        std::ostream& operator()(std::ostream& os, Dict const& ctx, mustache::partial const& v) const {
            auto it = ctx.find(v.value.to_string());
            if (it != ctx.end())
            {
                static const mustache_grammar<std::string::const_iterator> p;

                auto const& subtemplate = boost::get<std::string>(it->second);
                std::string::const_iterator first = subtemplate.begin(), last = subtemplate.end();

                mustache::sequence dynamic_template;
                if (qi::parse(first, last, p, dynamic_template))
                    return (*this)(os, Value{ctx}, dynamic_template);
            }
            return os << "#ERROR#";
        }

        std::ostream& operator()(std::ostream& os, Dict const& ctx, mustache::section const& v) const {
            auto it = ctx.find(v.control.to_string());
            if (it != ctx.end())
                boost::apply_visitor(std::bind(do_section(), std::ref(os), std::placeholders::_1, std::cref(v)), it->second);
            else if (!v.sense)
                (*this)(os, Value{/*Nil*/}, v.content);

            return os;
        }

        template <typename Ctx, typename T>
        std::ostream& operator()(std::ostream& os, Ctx const&/* ctx*/, T const&/* element*/) const {
            return os << "[TBI:" << __PRETTY_FUNCTION__ << "]";
        }

      private:
        struct do_section : boost::static_visitor<> {
            void operator()(std::ostream& os, Array const& ctx, mustache::section const& v) const {
                for(auto& item : ctx)
                    expander()(os, item, v.content);
            }
            template <typename Ctx>
            void operator()(std::ostream& os, Ctx const& ctx, mustache::section const& v) const {
                if (v.sense == truthiness(ctx))
                    expander()(os, Value(ctx), v.content);
            }
          private:
            static bool truthiness(Nil)                              { return false; }
            static bool truthiness(double d)                         { return 0. == d; }
            template <typename T> static bool truthiness(T const& v) { return !v.empty(); }
        };
    };

}

int myMain()
{
    std::cout << std::unitbuf;
    std::string input = "<ul>{{#time}}\n\t<li>{{> partial}}</li>{{/time}}</ul>\n "
        "<i>for all good men</i> to come to the {007} aid of "
        "their</bold> {{country}}. Result: {{^Res2}}(absent){{/Res2}}{{#Res2}}{{Res2}}{{/Res2}}"
        ;
    // Parser setup --------------------------------------------------------
    typedef std::string::const_iterator It;
    static const mustache_grammar<It> p;

    It first = input.begin(), last = input.end();

    try {
        mustache::sequence parsed_template;
        if (qi::parse(first, last, p, parsed_template))
        {
            std::cout << "Parse success\n";
        } else
        {
            std::cout << "Parse failed\n";
        }

        if (first != last)
        {
            std::cout << "Remaing unparsed input: '" << std::string(first, last) << "'\n";
        }

        std::cout << "Input:      " << input << "\n";
        std::cout << "Dump:       ";
        Dumping::dumper()(std::cout, parsed_template) << "\n";

        std::cout << "Evaluation: ";

        {
            using namespace ContextExpander;
            expander engine;

            Value const ctx = Dict { 
                { "time", Array {
                    Dict { { "partial", "gugus {{zeit}} (a.k.a. <u>{{title}}</u>)"},             { "title", "noon" },    { "zeit", "12:00" } },
                    Dict { { "partial", "gugus {{zeit}} (a.k.a. <u>{{title}}</u>)"},             { "title", "evening" }, { "zeit", "19:30" } },
                    Dict { { "partial", "gugus <u>{{title}}</u> (expected at around {{zeit}})"}, { "title", "dawn" },    { "zeit", "06:00" } },
                } },
                { "country", "ESP" },
                { "Res3", "unused" }
            };

            engine(std::cout, ctx, parsed_template);
        }
    } catch(qi::expectation_failure<It> const& e)
    {
        std::cout << "Unexpected: '" << std::string(e.first, e.last) << "'\n";
    }
}

这篇关于如何使用Boost.X pressive正确解析胡子?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆