1. Diagnostic需要提供哪些数据
出错处理和错误提示,是编译器开发过程中重要而繁琐的部分。
诊断信息的格式因编译器和IDE而不同。
SALVIA将采用Visual Studio的格式,即 文件 + 行列 + 类别(等级) + 编号 + 出错信息。例如:
d:\programming\salvia\sasl\test\cgllvm_test\function_test_basic.cpp(16): error C2061: syntax error : identifier 'te'
因此在出错分析的时候,也需要提供如上的一些信息。
2. 诊断信息Diagnostic Item
在以上信息中,文件名和行列号可以在词法分析的时候获得,我们将它作为属性附加在Token中。
类别和编号,对于同一个编译器而言是相对固定的,尽管我们可以用ID来表示,但是它并不直观,编译器检查也较少。与参数匹配时,也比较容易出错。
SASL中的诊断信息将每个错误都使用一个类型来表达:
class diagnostic_item { }; class unrecognized_identifier: public diagnostic_item { public: unrecognized_identifier& token( token_t tok ); private: static int level; static int id; static std::string description_template; private: std::string ident; size_t row, col; // Other properties };
这样的好处在于可以用Combinator的风格来撰写错误信息。例如这样:
diagnostic_chat.report<unrecognized_identifier>().token( err_tok );
并且由于编译器的保证也比较不容易写错。
但是这种写法也有一个很关键的问题,需要为每个错误都定义一个类,工作量很大。SASL对这一问题的处理,自然是传统的大杀器:运用脚本进行生成。
Clang使用了它内置的代码生成工具td来完成生成的工作。
3. 诊断信息管理器Diagnostic Chat
Chat是诊断信息的管理工具。它主要要完成以下需求:添加和清理诊断信息,以及在诊断信息的添加清理时提供回调操作。
后者是很有用的,尤其是在调试编译器的时候。你得分清楚究竟是真正的程序错误呢,还是编译器出了错。
Diagnostic Chat的原型如下:
class diagnostic_chat { public: template <typename T> T& report(); void add_report_diagnostic_handler( DiagnosticHandlerT handler ); };
同时,我们也将Treat Warning As Error,Error Count,Disable Warning,Stop compiling when error occurs等状态和功能所需要的支持添加到Chat中。
所以,Chat除了提供管理之外,也要具有相应的诊断信息的统计功能。
4. 过滤器Diagnostic Filter
Filter主要配合IDE使用,从Chat中取出符合条件的诊断信息。Error Count和Disable Warnings等功能也可以通过它来完成。
5. Formatter
Formatter用于将DiagnosticItems中的信息转换成人可读的字符串。目前SASL只打算支持Visual Studio的格式,但是相信支持GCC的格式以更好的和Eclipse等第三方IDE集成并不困难。
在C#里面,我们可以用“We need ‘{0}’ not ‘{1}’.”这样的方式来分离description template并延期的产生格式化的字符串。但是在C++中,这种做法并不容易。C的sprintf很难具有延期、渐增的绑定模板的特定,对自定义类型的字符串化的支持也不足,类型安全也比较差;而stream的话,也会面临着将好端端的格式化字符串割裂的问题。SASL使用了boost.format,从一定程度上搞定了这两个问题,从而像C#一样,使用格式化字符串的功能。