摘自《编译器原理,技术和工具,第二版》。 (“紫皮书”),作者是Aho,Lam,Sethi和Ullman:
图3.2:令牌示例pg。 112
[Token] [Informal Description] [Sample Lexemes]
if characters i, f if
else characters e, l, s, e else
comparison < or > or <= or >= or == or != <=, !=
id letter followed by letters and digits pi, score, D2
number any numeric constant 3.14159, 0, 6.02e23
literal anything but ", surrounded by "'s "core dumped"
在上面,他们将if
和else
分为自己的令牌类型。在我看到的大多数示例中,它们将是单个keyword
令牌类型,令牌的值将为if
或else
。为每个关键字使用单独的令牌类型而不是keyword
令牌类型有什么好处?
拥有comparison
这样的令牌类型有什么好处?为什么没有像下面这样的每种比较都具有令牌类型?
[Token] [Informal Description] [Sample Lexemes]
lt < <
gt > >
lte <= <=
gte >= >=
eq == ==
neq != !=
答案 0 :(得分:1)
当运算符在语法上相同时,关于各个运算符如何表示的观点也各不相同。即使没有真正的语法差异并且语义差异受到限制,许多人也会为不同的运算符编写单独的作品。
话虽如此,有些语言==
,>=
和<=
在语法上是不同的。在C(及其系列)中,这些运算符的优先级有所不同,因此可以编写不带括号的a <= b == b <= c
,尽管包含该表达式的代码不太可能在代码审查中幸免。 (即使带括号,该表达式也是有问题的。)在Python中,a <= b <= c
是有效的级联比较,而a <= b >= c
不是。等等
一般规则是,如果令牌在语言语法中具有独特的作用,则差异必须对解析器可见,并且解析器仅考虑令牌的类型,而不考虑其值。因此,在任何实际语法中,if
,then
和else
必须是不同的标记类型。
答案 1 :(得分:1)
编写解析器时,通常switch
是令牌类型。如果令牌类型不足以做出决定,那么这意味着您还必须检查case
内部的令牌值。如果令牌的值表示为字符串,则比较起来也将更加昂贵(即使字符串是interned,if-else-if序列的效率仍然不如switch高效)。在许多解析器生成器中,基于令牌的值进行决策是不可能的,或者比仅使用令牌的类型还要复杂。
为说明这一点,以下是手写解析器的摘录,其中不同的关键字具有不同的标记类型:
parse_statement() {
switch(current_token.type) {
case IF:
parse_if_statement(); break;
case WHILE:
parse_while_statement(); break;
//...
case ID: case NUMBER: case LITERAL:
parse_expression_statement(); break;
default:
syntax_error(); break;
}
}
还有相同的代码,并非如此:
parse_statement() {
switch(current_token.type) {
case KEYWORD:
if (current_token.value == "if") {
parse_if_statement();
} else if (current_token.value == "while") {
parse_while_statement();
// '} else if(...) {'s for other valid keywords go here
} else {
syntax_error();
}
// Other statement types that don't start with a keyword go here
case ID: case NUMBER: case LITERAL:
parse_expression_statement(); break;
default:
syntax_error(); break;
}
}
请注意附加的嵌套,现在有两个地方调用syntax_error
。
对于解析器生成器,它看起来像这样,具有不同的令牌类型:
statement
: IF condition body (ELSE body)?
| WHILE condition body
| ... | expression ';' ;
或者如果只有关键字标记类型,则这样:
statement
: if condition body (else body)?
| while condition body
| ... | expression ';' ;
if: {current_token.value == "if"} KEYWORD ;
else: {current_token.value == "else"} KEYWORD ;
while: {current_token.value == "while"} KEYWORD ;
这仅适用于支持语义谓词的解析器生成器。在许多其他情况下,这根本不可能。
当语法中不同的标记始终出现在同一位置时,即语法在它们之间没有区别,这是将它们合并为单个标记类型的便捷捷径。再次让我们比较比较类型和单个类型的语法:
comparison_exp: additive_exp COMPARISON additive_exp ;
并具有单独的类型:
comparison_exp: additive_exp comparison additive_exp ;
comparison: LT | GT | LTE | GTE | EQ | NEQ;
因此,如果您只有一种标记类型,则无需在语法中拼出所有选项。
与第一个问题相比,这是一个更为次要和主观的事情。