目前我有一些遗留代码,它生成操作码。如果代码有更多数量的宏,那么代码生成会花费很多时间(以小时计!!)。我已经完成了逻辑,他们通过搜索宏并替换其中的每个变量来处理宏,例如内联。
有没有一种方法可以在不操作字符串的情况下对其进行优化?
3 回答
在开始此类过程之前,您必须标记您的输入。(我不能高度推荐著名的龙书——即使是古代版本也经受住了时间的考验,2006 年的更新版本看起来很棒)。编译是最好分成更小的阶段的工作:如果您的第一阶段对标记执行词法分析,将行拆分为关键字、标识符、常量等,那么查找对宏的引用并查看它们要简单得多在符号表中。(使用 lex 或 flex 之类的工具或它们的现代等效工具之一为您完成这项工作也相对容易,而不是尝试从头开始)。
'线索'似乎是如果代码有更多数量的宏,那么代码生成需要很多时间。听起来这个过程在宏的数量上是线性的,这肯定太多了。我假设这个过程一次发生一行(如果您的语言允许,显然这具有巨大的价值,因为您不需要将程序视为一个巨大的字符串),并且伪代码看起来像
for(each line in the program)
{
for(each macro definition)
{
test if the macro appears;
perform replacement if needed;
}
}
这显然与宏定义的数量成比例。
使用标记化,它看起来像这样:
for(each line in the program)
{
tokenize the line;
for(each token in the line)
{
switch(based on the token type)
{
case(an identifier)
lookup the identifier in the table of macro names;
perform replacement as necessary;
....
}
}
}
这主要与程序的大小(而不是定义的数量)成比例 - 符号表查找当然可以使用更优化的数据结构来完成,而不是循环遍历它们,因此不再成为重要因素。第二步是像 yacc 和 bison(以及它们更现代的变体)这样的程序可以愉快地生成代码来做的事情。
事后思考:解析宏定义时,您也可以将它们存储为令牌流,并标记作为“占位符”名称的标识符以进行参数替换。扩展宏时,切换到该令牌流。(同样,像 flex 这样的东西很容易做到)。
我有一个应用程序,它有自己的语法。它支持典型编译器支持的所有类型的数据类型(甚至宏)。更准确地说,它是一种编译器,它通过将程序(使用该语法编写)作为输入来生成操作码。为了处理宏,它使用文本替换逻辑例如:
宏添加 (a:int, b:int)
int c = a + b
结束宏
// 程序总和
..
整数 x = 10, y = 10;
添加(x,y);
..
// 程序结束
更换后就会
// 程序总和
..
整数 x = 10, y = 10;
诠释 c = x + y
..
// 程序结束
此文本替换花费了很多时间,即用宏逻辑替换宏调用。有没有最佳的方法来做到这一点?
如果不了解更多的预处理器/解析/编译过程,这真的很难回答。一种想法是将宏名称存储在符号表中。解析时,首先根据该表检查文本标记,如果找到匹配项,则将替换写入新字符串,并通过解析器运行,然后继续解析宏紧括号后的原始文本。
根据您的操作码语法,另一个想法可能是 - 当您在解析时遇到宏定义时,生成操作码,但用占位符代替参数。然后,当解析器遇到对宏的调用时,生成用于评估参数的代码,并将该代码插入到预生成的宏代码中的占位符的位置。