分类: C/C++
2008-03-10 15:57:40
5.2 数值分析器(终结符)
Spirit预定义大量的数值分析器,包括符号(无符号)整数分析器,符号(无符号)浮点分析器等。数值分析器可进行进制,小数点左右精度等参数的配置,此外数值分析器的行为是通过数值分析器策略来控制,用户可以自定义策略来实现不同的操作。
uint_parser 无符号整数分析器 ( boost_operator.cpp )
template <
typename T = unsigned,
int Radix = 10,
unsigned MinDigits = 1,
int MaxDigits = -1 >
struct uint_parser { /*...*/ };
模版参数:
T 数值分析器基本类型
Radix 数值进制
MinDigits 允许的最少位数
MaxDigits 允许的最大位数
预定义的数值分析器:
bin_p 二进制数值分析器 uint_parser
oct_p 八进制数值分析器 uint_parser
uint_p 十进制数值分析器 uint_parser
hex_p 十六进制数值分析器 uint_parser
int_parser 符号数分析器
real_parser 实数分析器
template<
typename T = double,
typename RealPoliciesT = ureal_parser_policies
struct real_parser;
模版参数类型:
T 实数类型 float, double ,long double或用户自定义类型
RealPoliciesT 实数分析器策略
预定义实数分析器:
ureal_p 无符号实数分析器
real_p 实数分析器
strict_ureal_p 严格无符号实数分析器
strict_real_p 严格实数分析器
严格实数要求 小数点 必须出现。
实数分析器策略:
1. parse_sign 分析符号前缀'+', '-'
2. parse_n 分析小数点左边整数部分
3. parse_dot 分析小数点
4. parse_frac_n 分析小数点右边整数部分
5. parse_exp 分析指数前缀'E' , 'e'
6. parse_exp_n 分析指数部分
这些子分析步骤的交互由另外的三个策略进行控制:
allow_leading_dot 允许前导小数点,如 .1342
allow_trailing_dot 允许尾部小数点,如 1324.
expect_dot 数值部分必须由小数点
策略:
策略名称 修改部分
ureal_parser_policies
real_parser_policies : ureal_parser_policies parse_sign
strict_urel_parser_policies : ureal_parser_policies expect_dot = true
strict_real_parser_policies : real_parser_policies expect_dot = true
用户自定义的实数分析器策略可以通过修改预定义的实数策略来完成特殊的操作。
如分析千位分隔、最多两位小数、没有指数部分的实数。 thousand_seperated.cpp
5.3 规则(Rule)
EBNF 产生式规则,由左边的非终结符 和 右边的EBNF C++表达式组成。
template<
typename ScannerT = scanner<>,
typename ContextT = parser_context<>,
typename TagT = parser_address_tag>
class rule;
模版参数:
ScannerT 扫描器类型 scanner<>
ContextT 分析器上下文 parser_context<>
TagT 规则标签 parser_address_tag<>
在1.8.0这个版本中, ScannerT,ContextT和TagT可以以任意顺序排列 。如果某个模板参数缺失,则适用默认的参数。
在1.8.0中,规则可以使用一个或者多个扫描器类。引入多扫描器是为了支持语法层面和词法层面的不同分析操作。
typedef scanner_list
rule
assert( parse("abcdefghijk", r).full );
assert( parse("a b c d e f g h i j k", r, space_p).full );
为了支持多扫描器,必须在boost头文件引用前定义宏
# define BOOST_SPIRIT_RULE_SCANNERTYPE_LIMIT 3 // 多扫描器最大限制
当规则在EBNF表达式右边引用时,表达式使用的是规则的引用。规则是C++的一个特例,它没有拷贝和赋值语义,不能以值进行存储和引用。必须显示调用copy()函数来实现拷贝操作。规则是动态的,对一个规则进行二次赋值会引起它的析构和重定义。
规则本质上来说是一个动态分析器。
为了识别,可以给规则加上标签(Tag),尤其是在分析树和AST树中要判断是那条规则产生该结点时。每条规则都有一个类型为parser_id的ID,通过成员函数id()获取。
parser_address_tag 使用rule的内存地址作为rule tag
parser_tag
dynamic_parser_tag 使用动态数值作为rule tag (动态)
5.4 空串 (Epsilon)
epsilon_p , eps_p Epsilon空串
空串用途:
1. 零长度匹配
通常用于无条件触发一个语义动作。如:
R = A | B | C | eps_p[ error ] ; // error if A, B or C fails to match
2. 语义断言 Semantic Predicate
语义断言允许你在语法的任意点上挂接函数,eps_p接受零元函数对象,该函数对象通常用于解决语法二义性的一个测试。如果函数对象的结果为false,则分析失败。
eps_p(f) >> rest; // 函数f触发调用,检测一个符号是否在符号表中,如果返回true,则继续分析
3. 语法断言(句意断言) Syntactic Predicate
类似于语义断言,语法断言在推演后续产生式之前检测语法断言。这时eps_p的输入为条件分析器,如:
eps_p(p) >> rest ; // 分析器p进行语法检测,如果输入匹配p,则进行后续推演。eps_p空串不消耗任何输入。
eps_p( '0' ) >> oct_p ; // 匹配前导'0'的八进制数值,如果发现一个'0',epsilon_p将报告一个零长度的成功匹配。
4. 基本类型参数
eps_p接受c++内置类型作为参数。如char, wchar_t, char *, wchar *。
5. 抑制语义动作 Inhibiting Semantic Actions
在语法断言中,直接或间接作用于条件分析器的语义动作都不会调用,但是作用于eps_p的语义动作总是被调用(因为eps_p总是返回true)。如:
eps_p( p[f] ) // f not called
eps_p( p ) [f] // f is called
eps_p[f] // f is called
作为条件分析器要求必须不具有副作用(语义动作)。条件分析器的作用是通过 前向匹配(forward-match) 输入来消除语法二义性。二义性和语义动作不能够很好的结合使用,在一个二义性文法中,会产生回溯。当回溯产生时,已触发的语义动作无法回溯。
6. 反操作符 Negation
~eps_p ~~eps_p
对 结果 进行取反操作。
5.5 Directives ( 指示器)
形式: directive[expression]
Spirit预定义少量directives,框架使用者可以自定义directive. Directive控制expression的行为。
lexeme_d
功能:禁用空格忽略。
在短语级别,分析器将忽略空格和注释。当我们想在字符界别而不是短语级别操作时,使用lexeme_d。将分析器封闭在lexeme_d[]中初始分析器在字符级别工作。如:
integer = lexeme_d[ !(ch_p('+') | '-') >> +digit ];
lexeme _d指定使得封闭的分析器处于字符界别进行操作。如果没有使用lexeme_d的话,rule( !(ch_p('+') | '-') >> +digit ) 将会把"1 2 3 4 5"分析为"12345",允许数字之间出现的不正确的空格。
as_lower_d
功能:忽略大小写,将所有字符转换为小写字符处理。
例子: as_lower_d[ "begin" ] 匹配 begin, BEGIN ....。
注意:as_lower_d只是将输入转换为小写字符处理,它不能够将[]中的字符转换为小写字符处理。如果as_lower_d[ ]中出现大写字符,则会出现分析错误。如:as_lower_d[ 'X' ],该分析器永远无法匹配成功,因为分析器想要匹配大写字符'X',但是as_lower_d不支持这种功能。
no_actions_d
功能:禁止语义动作的触发。
directive工作原理:
lexeme_d,as_lower_d和no_actions_d的原理主要时通过将 扫描器策略(Sanner Policy) 组合起来实现不同的功能。规则rule属于一个特殊的分析器(更精确的说是一个或多个分析器),如果将规则放到lexeme_d,as_lower_d或no_actions_d当中,除非你将扫描器和规则关联起来,否则编译器会出现扫描器不匹配错误。( why?)
longest_d
功能:匹配所有可选项,获取一个最长匹配。(仅对于或运算符)
或运算符在spirit框架当中采取短路匹配方式来进行匹配。也就是说对于规则:
rule<> r1 = p1 | p2 | p3 ... | pn, 对于i <= n,如果对于任意的j < i,pj都无法匹配输入,但是pi匹配输入,那么后续的分析器就无需进行匹配。虽然spirit或运算符的短路行为对于具有优先情况的匹配操作可能是非常好的, 但是也存在这样一种情况, 如:
rule<> r2 = real_p | int_p;
对于输入为"1234",由于整数本身也是实数, 因此该输入永远只匹配real_p,不会匹配int_p;但是如果将规则转变为:
rule<> r2 = int_p | real_p ;
对于输入为"1234",这时输入永远只匹配int_p,不会匹配real_p。但如果输入为: "124321.3242",这种情况下由于或运算符的短路行为,只会用int_p来匹配输入的前部分"124321("."分隔)。这并不是我们想要的结果,通过使用longest_d将规则封装起来,这样就会用所有的选择项来匹配输入并选取一个最长匹配项real_p。
shortest_d
功能:与longest_d相反
limit_d ,min_limit_d和max_limit_d
功能:限定分析器结果范围(对于数值分析器)
虽然数值分析器可以通过模版参数限定数值位数,但是要限定结果范围只能通过limit_d,min_limit_d和max_limit_d来限定。使用方法如下:
limit_d(min, max)[expression]
min_limit_d(min) [expression] min_limit_d(1900u)[uint_p] >= 1900u
max_limit_d(max) [expression] max_limit_d(1900u)[uint_p] <= 1900u