Boost学习之语法解析器 持续改进… 例五的代码用起来很爽吧,不算注释的话100行不到,已经有个脚本的雏形了。只是…只是有个小问题,因为我们设置了跳过空格,这对于语句来说是必须的,但却带来了一个副作用。 试试把szEqus里的变量名中间加个空格,比如改成”R ad = P I*2.0/3.0″,这样的语句居然也能正确解析,这显然不是我们想要的(要的就是这种效果?!!偶无语…)。 那么怎样才能解析变量名时不许跳过空格,而解析语句的又允许跳过呢(搞双重标准)?下面介绍的命令就可以帮上忙了,首先赶快在没人发现这个错误之前把它搞定先: 把所有的变量名规则(factor规则定义里有一个,rlEqu规则定义里有一个)用lexeme_d包裹起来: lexeme_d[(alpha_p >> *(alnum_p))] 再测试,嗯,现在不允许出现含有空格的变量名了。 下面介绍各种预置命令 使用形式: 命令[解析器表达式] lexeme_d 不跳过空白字符,当工作于语法级时,解析器会忽略空白字符,lexeme_d使其临时工作于字符级 如整数定义应该是: integer = lexeme_d[ !(ch_p(‘+’) | ‘-‘) >> +digit ];,这样可以防止”1 2 345″被解析为”12345″ as_lower_d 忽略大小写,解析器默认是大小写敏感的,如果要解析象PASCAL一样的大小写不敏感的语法,使用r = as_lower_d[“begin”];(注,里面的参数都得小写) no_actions_d 停止触发Actor longest_d 尝试最长匹配 如number = integer | real;用它匹配123.456时,integer会匹配123直到遇到小数点结束,使用number=longest_d[integer | real];可以避免这个问题。 shortest_d 与longest_d相反 limit_d 定义范围,用法limit_d(min, max)[expression] 如 uint_parser<int, 10, 2, 2> uint2_p; r = lexeme_d [ limit_d(0u, 23u)[uint2_p] >> ‘:’ // Hours 00..23 >> limit_d(0u, 59u)[uint2_p] >> ‘:’ // Minutes 00..59 >> limit_d(0u, 59u)[uint2_p] // Seconds 00..59 ]; min_limit_d/max_limit_d 定义最小/最大值,用法:min_limit_d(min)[expression] 例七,牛叉型解析器 相对于Spirit预置的一些简单解析器,它也提供了很多功能更强大的“牛叉型”解析器。现介绍如下: f_ch_p 语法:f_ch_p(ChGenT chgen) 作用:和ch_p类似,它解析的字符由chgen的返回值决定,chgen是一个类型为”CharT func()”的函数(或函数对象) 例如:char X(){return ‘X’;} f_ch_p(&X); f_range_p 语法:f_range_p(ChGenAT first, ChGenBT last) 作用:和range_p类似,它由first和last两个函数(或函数对象)的返回值决定解析的字符范围。 f_chseq_p 语法:f_chseq_p(IterGenAT first, IterGenBT last) 作用:和chseq_p类似,同样由first和last两个函数(或函数对象)的返回值决定起始和终止迭代器。 f_str_p 语法:f_str_p(IterGenAT first, IterGenBT last) 作用:和str_p类似,参数同f_chseq_p if_p 语法:if_p(condition)[then-parser].else_p[else-parser],其中.else_p可以不要 作用:如果condition成立,就使用then-parser,否则用else-parset 例如:if_p(“0x”)[hex_p].else_p[uint_p] for_p 语法:for_p(init, condition, step)[body-parser] 作用:init和step是一个无参数的函数或函数对象,各参数与for的作用类似(先init,再检查condition,有效则执行body-parser及step,再检查condition…) 例如:for_p(var(i)=0, var(i) < 10, ++var(i) ) [ int_p[var(sum) += arg1] ] while_p, do_p 语法:while_p(condition)[body-parser] 及 do_p[body-parser].while_p(condition) 作用:条件循环,直接condition不成立为止。 select_p, select_fail_p 语法:select_p(parser_a , parser_b /* … */, parser_n); 作用:从左到右接顺序测试各解析器,并得到匹配的解析器的序号(0表示匹配parser_a,1匹配parser_b…) 例如:见switch_p例 switch_p 语法:switch_p(value)[case_p<value_a>(parser_a),case_p<value_b>(parser_b),…,default_p(parser_def)] 作用:按value的值选择解析器 例如:下例中匹配的形式为:字符a后是整数,b后是个逗号,c后跟着”bcd”,d后什么也没有。 int choice = -1; rule<> rule_select = select_fail_p(‘a’, ‘b’, ‘c’, ‘d’)[assign_a(choice)] >> switch_p(var(choice)) [ case_p<0>(int_p), case_p<1>(ch_p(‘,’)), case_p<2>(str_p(“bcd”)), default_p ]; c_escape_ch_p, lex_escape_ch_p 语法:c_escape_ch_p 作用:和ch_p类似,其牛叉的地方在于能解析C语言里的转义字符:b, , , f, , \, “, ‘, xHH, OOO 例如:confix_p(‘”‘, *c_escape_ch_p, ‘”‘) repeat_p 语法、作用: repeat_p (n) [p] 重复n次执行解析器p repeat_p (n1, n2) [p] 重复n1到n2次解析器p repeat_p (n, more) [p] 至少重复n次解析 例如:检验是否是有效的文件名 valid_fname_chars = /*..*/; filename = repeat_p(1, 255)[valid_fname_chars]; confix_p 语法:confix_p(open,expr,close) 作用:解析独立素,如C语言里的字符串,注释等,相当于open >> (expr – close) >> close 例如:解析C注释confix_p(“/*”, *anychar_p, “*/”) comment_p,comment_nest_p 语法:comment_p(open,close),如果close不指定,默认为回车 作用:confix_p的辅助解析器,comment_p遇到第一个close时即返回,而comment_nest_p要open/close对匹配才返回。 例如: comment_p(“https://”) C++风格注释 comment_nest_p(‘{‘, ‘}’)|comment_nest_p(“(*”, “*)”) pascal风格注释 list_p 语法:list_p(paser,delimiter) 作用:匹配以delimiter作为分隔符的列表 regex_p 语法:regex_p(“正则表达式”) 作用:使用正则表达式来匹配字符串(强强联手啊~~啥也不说了) symbols类 定义: template < typename T = int, typename CharT = char, typename SetT = impl::tst<t, chart> > class symbols; 初始化方式: symbols<> sym; sym = “pineapple”, “orange”, “banana”, “apple”, “mango”; sym.add(“hello”, 1)(“crazy”, 2)(“world”, 3); 作用:匹配字符串(CharT*)返回对应的整数(T) 例如: struct Show{ void operator()( int n ) const { cout << n; } }; symbols<> sym; sym.add(“零”,0) (“一”,1) (“二”,2) (“三”,3) (“四”,4) (“五”,5) (“六”,6) (“七”,7) (“八”,8) (“九”,9); parse(“二零零八”,*(sym[Show()])); functor_parser 作用:可以方便地用它来创建一个解析器 例如:见下例 演示怎样自己写一个解析器,解析一个整数 struct number_parser { typedef int result_t; //定义解析器结果类型 //参数是:扫描器,结果 template <typename ScannerT> std::ptrdiff_t operator()(ScannerT const& scan, result_t& result) const { if (scan.at_end()) //如果结果或出错,返回-1 return -1; char ch = *scan; if (ch < ‘0’ || ch > ‘9’) return -1; result = 0; std::ptrdiff_t len = 0; do //解析字符串,得到结果 { result = result*10 + int(ch – ‘0’); ++len; ++scan; } while (!scan.at_end() && (ch = *scan, ch >= ‘0’ && ch <= ‘9’)); return len; //返回解析的字符串长度 } }; //用functor_parser包装成解析器 functor_parser<number_parser> number_parser_p;
2024最新激活全家桶教程,稳定运行到2099年,请移步至置顶文章:https://sigusoft.com/99576.html
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。 文章由激活谷谷主-小谷整理,转载请注明出处:https://sigusoft.com/77262.html