| 网管联盟 | 网管论坛 | 网管u家 | 网管博客 | 网管软件 | 网管求职 | 小游戏 | 网管搜索 | 网管原创 | 网管聚合 | 网管读摘 | 网管焦点 | 世界素材 | 会员投稿 | 会员中心 |
![]() |
| Windows Linux Cisco 网络技术 数据库 黑客攻防 DotNet Java PHP 认证 新闻资讯 服务器 存储资讯 网络设备 网管学堂 技术专题 焦点 网吧频道 |
更好的输入函数
使用原来的错误消息,很难判断语义的错误。当然,这个例子非常容易修复,因为我们立即就可以找出有错误的那一行。在更加复杂的语法和对应输入中,这可能并不简单。让我们编写一个输入函数来从文件中读取相应的行。
Flex 具有一个非常有用的宏 YY_INPUT,它负责为符号解释读入数据。我们可以在 YY_INPUT 宏中添加一个对 GetNextChar() 函数的调用,后者从文件中读取数据,并保留了下一个要读取的字符的位置信息。GetNextChar() 使用了一个缓冲区来存放一行输入。这两个变量保存了当前行号和该行中下一个字符的位置: 网管有家www.bitscn.net
清单 8. 更好的 Flex YY_INPUT 宏
#define YY_INPUT(buf,result,max_size) {\
result = GetNextChar(buf, max_size); \
if ( result <= 0 ) \
result = YY_NULL; \
}
网管有家www.bitscn.net
|
使用这个增强的错误打印函数 PrintError()(在前面讨论过,它可以很好地显示有问题的输入行,完整的 PrintError() 源代码请参看 示例源代码),我们就具有了一个用户友好的消息,它显示了下一个字符的位置: 网管u家u.bitsCN.com
清单 9. 更好的 Flex 错误:字符位置
|....+....:....+....:....+....:....+....:....+....:....+
1 |a = 3;
2 |3 aa = a * 4;
...... !.....^
Error: syntax error, unexpected IDENTIFIER, expecting SEMICOLON
3 |b = aa / ( a - 3 );
...... !.......^
Error: reference to unknown variable 'aa'
...... !.................^
Error: division by zero!
|
网管有家www.bitscn.net
这个示例函数可以从其他函数(例如 ReduceDiv())中进行调用,从而打印语义错误,例如 division by zero 或 unknown identifiers。 网管联盟bitsCN@com
如果我们希望标记一下最后使用的符号,就可以对 Flex 规则进行扩展,并修改错误的打印。函数 BeginToken() 和 PrintError()(二者都可以在示例源代码中找到)是关键:BeginToken() 是由每条规则进行调用的,这样它就可以记住每个符号的开始和结束,每次打印错误时都会调用 PrintError()。这样,我们就可以生成一条有用的消息了,例如:
网管u家www.bitscn.net
清单 10. 更好的 Flex 错误:表示确切的符号位置
2 |3 aa = a * 4;
...... !..^^............
Error: syntax error, unexpected IDENTIFIER, expecting SEMICOLON
网管网www.bitscn.com
|
网管联盟bitsCN@com
缺点 网管论坛bbs_bitsCN_com
所生成的词法解析器可能会在检测到某个符号之前读入多个字符。因此,这个过程不可能精确地显示确切的位置。它最终取决于为 Flex 所提供的规则。规则越复杂,位置的精确程度就越低。这个例子中的规则可以由 Flex 通过提前查找一个字符来进行处理,这会让位置的预测更加精确。
网管朋友网www_bitscn_net
Bison 的定位机制 网管网www_bitscn_com
下面让我们来看一下 division by zero 这个错误。最后一次符号读取(结束括号)并不是这个错误的根源。表达式 (a-3) 的值就是 0。对于更好的错误消息来说,我们需要知道表达式的位置。要实现这种功能,我们可以在 YYLTYPE 类型的全局变量 yylloc 中提供这个符号的确切位置。使用宏 YYLLOC_DEFAULT(请参看 Bison 文档 中默认的定义),Bison 可以计算出某个表达式的位置。
记住,只有当您在文法中使用位置时才会定义类型。这是一个常见的错误。
网管u家bitscn.net
默认的位置类型 YYLTYPE 如清单 11 所示。我们可以对这个类型重新进行定义,使其包括更多信息,例如 Flex 所读取的文件名。 网管u家www.bitscn.net
清单 11. 默认位置类型 YYLTYPE
typedef struct YYLTYPE
{
int first_line;
int first_column;
int last_line;
int last_column;
} YYLTYPE;
|
在上一节中,我们看到了 BeginToken() 函数,它是在新符号开始时调用的。此时就应该存储这个位置了。在我们的例子中,一个符号不能跨越多行,因此 first_line 和 last_line 是相同的,它们都保存了当前的行号。其他属性有符号的起点(first_column)和终点(last_column),这是通过符号的起点和长度计算出来的。
网管有家www.bitscn.net
要使用这个位置,我们必须对规则处理函数进行处理,如清单 12 所示。符号 $3 的位置是通过 @3 进行引用的。为了防止拷贝这个规则中的整个结构,我们生成了一个指针 &@3。这看起来可能有点奇怪,但却是正确的。
清单 12. 记住规则中的位置
| expression DIV expression
{
$ = ReduceDiv($1, $3, &@3);
}
|
在处理函数中,我们获得了一个指向保存了位置信息的 YYLTYPE 结构的指针,这样可以生成一条很好的错误消息。
网管u家u.bitsCN.com
清单 13. 在 ReduceDiv 中使用保存的位置
extern
double ReduceDiv(double a, double b, YYLTYPE *bloc) {
if ( b == 0 ) {
PrintError("division by zero! Line %d:c%d to %d:c%d",
bloc->first_line, bloc->first_column,
bloc->last_line, bloc->last_column);
return MAXFLOAT;
}
return a / b;
}
网管朋友网www_bitscn_net
|
现在错误消息可以帮助我们来定位问题了。除零操作错误在第 3 行的第 10 列到 18 列之间。
网管下载dl.bitscn.com
清单 14. 更好的 ReduceDiv() 错误消息
|....+....:....+....:....+....:....+....:....+....:....+
1 |a = 3;
2 |3 aa = a * 4;
...... !..^^...........
Error: syntax error, unexpected IDENTIFIER, expecting SEMICOLON
3 |b = aa / ( a - 3 );
...... !....^^...............
Error: reference to unknown variable 'aa'
...... !.................^..
Error: division by zero! Line 3:10 to 3:18
final content of variables
Name------------------ Value----------
'a ' 3
'b ' 3.40282e+38
'aa ' 0
|
网管u家u.bitsCN.com
结束语
中国网管联盟bitsCN.com
Flex 和 Bison 是用来解析文法的一对功能强大的组合。通过使用本文中介绍的技巧,我们可以构建更好的解释器,它们可以生成像您自己喜欢的编译器中一样的有用的、容易理解的错误消息。 网管u家u.bitscn@com
|
0
|
评论加载中…