我一直在寻找一种可以将 C 代码表达式转换为表单的工具:
a = (A) ? B : C;
if
使用/else
语句进入“默认”语法:
if (A)
a = B
else
a = C
有人知道能够进行这种转换的工具吗?
我使用 GCC 4.4.2 并创建一个预处理文件,-E
但不希望其中包含此类结构。
编辑:以下代码也应该转换:
a = ((A) ? B : C)->b;
我一直在寻找一种可以将 C 代码表达式转换为表单的工具:
a = (A) ? B : C;
if
使用/else
语句进入“默认”语法:
if (A)
a = B
else
a = C
有人知道能够进行这种转换的工具吗?
我使用 GCC 4.4.2 并创建一个预处理文件,-E
但不希望其中包含此类结构。
编辑:以下代码也应该转换:
a = ((A) ? B : C)->b;
Coccinelle可以很容易地做到这一点。
Coccinelle 是一个程序匹配和转换引擎,它提供语言 SmPL(语义补丁语言)用于在 C 代码中指定所需的匹配和转换。Coccinelle 最初的目标是在 Linux 中执行附带演变。这种演变包括客户端代码中响应库 API 演变所需的更改,并且可能包括修改,例如重命名函数、添加其值在某种程度上依赖于上下文的函数参数以及重新组织数据结构。除了附带演变之外,Coccinelle 还成功地用于(我们和其他人)查找和修复系统代码中的错误。
编辑: 语义补丁的一个例子:
@@ expression E; constant C; @@
(
!E & !C
|
- !E & C
+ !(E & C)
)
从文档中:
模式 !x&y。这种形式的表达式几乎总是没有意义的,因为它结合了布尔运算符和位运算符。特别是,如果 y 的最右边位为 0,则结果将始终为 0。此语义补丁侧重于 y 为常数的情况。
你在这里有一组很好的例子。
邮件列表非常活跃且很有帮助。
Coccinelle 的以下语义补丁将进行转换。
@@
expression E1, E2, E3, E4;
@@
- E1 = E2 ? E3 : E4;
+ if (E2)
+ E1 = E3;
+ else
+ E1 = E4;
@@
type T;
identifier E5;
T *E3;
T *E4;
expression E1, E2;
@@
- E1 = ((E2) ? (E3) : (E4))->E5;
+ if (E2)
+ E1 = E3->E5;
+ else
+ E1 = E4->E5;
@@
type T;
identifier E5;
T E3;
T E4;
expression E1, E2;
@@
- E1 = ((E2) ? (E3) : (E4)).E5;
+ if (E2)
+ E1 = (E3).E5;
+ else
+ E1 = (E4).E5;
DMS Software Reengineering Toolkit可以通过应用程序转换来做到这一点。
与您的特定示例匹配的特定 DMS 转换:
domain C.
rule ifthenelseize_conditional_expression(a:lvalue,A:condition,B:term,C:term):
stmt -> stmt
= " \a = \A ? \B : \C; "
-> " if (\A) \a = \B; else \a=\C ; ".
您需要另一条规则来处理您的其他情况,但同样容易表达。
转换对源代码结构而不是文本进行操作,因此布局和注释不会影响识别或应用。规则中的引号不是传统的字符串引号,而是元语言引号,将规则语法语言与用于指定要更改的具体语法的模式语言分开。
如果您打算保留预处理指令,则会出现一些问题。由于您显然愿意使用预处理器扩展的代码,因此您可以要求 DMS 将预处理作为转换步骤的一部分;它内置了完整的 GCC4 和 GCC4 兼容的预处理器。
正如其他人所观察到的,这是一个相当简单的案例,因为您指定它在完整语句的级别上工作。如果您想摆脱与此语句相似的任何赋值代码,并将此类赋值嵌入各种上下文(初始化程序等)中,您可能需要一组更大的转换来处理各种特殊情况,您可能需要制造其他代码结构(例如,适当类型的临时变量)。像 DMS 这样的工具的好处是它可以显式地计算任意表达式的符号类型(因此是任何所需临时的类型声明),并且您可以相当直接地编写这样一个更大的集合并应用所有它们。
尽管如此,我不确定执行三元条件表达式消除操作的真正价值。一旦编译器得到结果,您可能会得到类似的目标代码,就好像您根本没有进行转换一样。毕竟,编译器也可以应用等价保持转换。
不过,总的来说,定期进行更改显然是有价值的。
(DMS 可以将源到源的程序转换应用于许多语言,包括 C、C++、Java、C# 和 PHP)。
我不知道三元运算符内置在语言规范中作为if
逻辑的快捷方式......我能想到的唯一方法是手动查找这些行并将其重写为表单在哪里if
使用...作为普遍共识,三元运算符的工作方式是这样的
expr_is_true ? exec_if_expr_is_TRUE:exec_if_expr_is_FALSE;
如果表达式被评估为真,则执行 and 之间的部分?
,否则执行and:
之间的最后部分。如果表达式被评估为假,情况正好相反:
;
expr_is_false ? exec_if_expr_is_FALSE:exec_if_expr_is_TRUE;
如果这些语句像这样非常有规律,为什么不通过一个小的 Perl 脚本来运行你的文件呢?对于您的示例行,执行查找和转换的核心逻辑很简单。这是一个简单的方法:
use strict;
while(<>) {
my $line = $_;
chomp($line);
if ( $line =~ m/(\S+)\s*=\s*\((\s*\S+\s*)\)\s*\?\s*(\S+)\s*:\s*(\S+)\s*;/ ) {
print "if(" . $2 . ")\n\t" . $1 . " = " . $3 . "\nelse\n\t" . $1 . " = " . $4 . "\n";
} else {
print $line . "\n";
}
}
exit(0);
你会像这样运行它:
perl transformer.pl < foo.c > foo.c.new
当然,如果文本模式不像您发布的那样规则,它会变得越来越难。但免费、快速且易于尝试。