我知道词法分析器将输入标记化并将其存储在流中,或者至少这是我所理解的。不幸的是,我读过的几乎所有文章都只谈到了简单的表达方式。我感兴趣的是如何标记类似的东西:
if (fooBar > 5) {
for (var i = 0; i < alot.length; i++) {
fooBar += 2 + i;
}
}
请注意,这是伪代码。
问题:我想知道lexer创建的令牌的数据结构如何?我真的不知道上面给出的代码嵌套的例子。一些例子会很好。
答案 0 :(得分:2)
首先,不一定存储令牌。有些编译器会将令牌存储在表或其他数据结构中,但对于简单的编译器(如果存在这样的事情),在大多数情况下,词法分析器可以返回要解析的下一个令牌的类型就足够了然后在某些情况下,解析器可能会向词法分析器询问令牌的实际文本。
如果我们使用您的示例代码,
if (fooBar > 5) {
for (var i = 0; i < alot.length; i++) {
fooBar += 2 + i;
}
}
此示例中第一个标记的类型可能被定义为TOK_IF,对应于&#34; if&#34;关键词。下一个标记可能是TOK_LPAREN,然后是TOK_IDENT,然后是TOK_GREATER,然后是TOK_INT_LITERAL,依此类推。作为词法分析器(或标记化器)代码的作者,您应该将这些类型确切定义为什么。 (请注意,大约有一百万种不同的工具可以帮助您避免手动提出这些细节的繁琐工作。)
除了TOK_IDENT和TOK_INT_LITERAL之外,我们到目前为止看到的令牌完全由它们的类型定义。对于这两个,我们需要能够向词法分析器询问基础文本,以便我们可以评估令牌的值。
因此,在伪代码中处理IF语句的解析器的一小部分可能看起来像:
...
switch(lexer.GetNextTokenType())
case TOK_IF:
{
// "if" statement
if (lexer.GetNextTokenType() != TOK_LPAREN)
throw SyntaxError('( expected');
ParseRelationalExpression(lexer);
if (lexer.GetNextTokenType() != TOK_RPAREN)
throw SyntaxError(') expected');
...
等等。
如果编译器确实选择实际存储令牌以供以后参考,并且某些编译器会执行以下操作:为了实现更有效的回溯,一种方法是使用类似于以下的结构
struct {
int TokenType;
char* TokenStart;
int TokenLength;
}
这些容器可能是链表或std :: vector(假设是C ++)。