views:

351

answers:

6

I was looking for a tool that can convert C code expressions for the form:

a = (A) ? B : C;

into the 'default' syntax with if/else statements:

if (A)
  a = B
else
  a = C

Does someone know a tool that's capable to do such a transformation?

I work with GCC 4.4.2 and create a preprocessed file with -E but do not want such structures in it.

Edit: Following code should be transformed, too:

a = ((A) ? B : C)->b;
A: 

you can write a application that serves this purpose

Luiscencio
@Luiscencio: LOL! Now, _that_ was insightful!
mjv
it might be lighter and focused over exclusive requirements
Luiscencio
=] one downvote, I feel flattered...
Luiscencio
It's actually a nice idea!
Andrei Ciobanu
+11  A: 

Coccinelle can do this quite easily.

Coccinelle is a program matching and transformation engine which provides the language SmPL (Semantic Patch Language) for specifying desired matches and transformations in C code. Coccinelle was initially targeted towards performing collateral evolutions in Linux. Such evolutions comprise the changes that are needed in client code in response to evolutions in library APIs, and may include modifications such as renaming a function, adding a function argument whose value is somehow context-dependent, and reorganizing a data structure. Beyond collateral evolutions, Coccinelle is successfully used (by us and others) for finding and fixing bugs in systems code.

EDIT: An example of semantic patch:

@@ expression E; constant C; @@
(
  !E & !C
|
- !E & C
+ !(E & C)
)

From the documentation:

The pattern !x&y. An expression of this form is almost always meaningless, because it combines a boolean operator with a bit operator. In particular, if the rightmost bit of y is 0, the result will always be 0. This semantic patch focuses on the case where y is a constant.

You have a good set of examples here.

The mailing list is really active and helpful.

LB
Mind posting a semantic patch as an example?
ephemient
An example would be great.
tur1ng
I've edited my answer with a simple example.
LB
A: 

I am not aware of such a thing as the ternary operator is built-into the language specifications as a shortcut for the if logic... the only way I can think of doing this is to manually look for those lines and rewrite it into the form where if is used... as a general consensus, the ternary operator works like this

expr_is_true ? exec_if_expr_is_TRUE : exec_if_expr_is_FALSE;

If the expression is evaluated to be true, execute the part between ? and :, otherwise execute the last part between : and ;. It would be the reverse if the expression is evaluated to be false

expr_is_false ? exec_if_expr_is_FALSE : exec_if_expr_is_TRUE;
tommieb75
A: 

If the statements are very regular like this why not run your files through a little Perl script? The core logic to do the find-and-transform is simple for your example line. Here's a bare bones approach:

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);

You'd run it like so:

perl transformer.pl < foo.c > foo.c.new

Of course it gets harder and harder if the text pattern isn't as regular as the one you posted. But free, quick and easy to try.

Ian C.
This does not work for: a = ((A) ? B : C)->b;
tur1ng
I did warn you it was only for the example you provided. :) Rather than try to write some uber regular expression you could make that a second elsif case fairly easily: m/(\S+)\s*=\s*\(\s*\((\s*\S+\s*)\)\s*\?\s*(\S+)\s*:\s*(\S+)\s*\)\s*->\s*(\S+)\s*;/ -- the dereference call is match group $4 now.
Ian C.
@tur1ng: I think the example with the-> probably can't bedone without a temporary variable.
Brian Postow
+3  A: 

The following semantic patch for Coccinelle will do the transformation.

@@
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;
Nico
+1  A: 

The DMS Software Reengineering Toolkit can do this, by applying program transformations.

A specific DMS transformation to match your specific example:

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 ; ".

You'd need another rule to handle your other case, but it is equally easy to express.

The transformations operate on source code structures rather than text, so layout out and comments won't affect recognition or application. The quotation marks in the rule not traditional string quotes, but rather are metalinguistic quotes that separate the rule syntax language from the pattern langu age used to specify the concrete syntax to be changed.

There are some issues with preprocessing directives if you intend to retain them. Since you apparantly are willing to work with preprocessor-expanded code, you can ask DMS to do the preprocessing as part of the transformation step; it has full GCC4 and GCC4-compatible preprocessors built right in.

As others have observed, this is a rather easy case because you specified it work at the level of a full statement. If you want to rid the code of any assignment that looks similar to this statement, with such assignments embedded in various contexts (initializers, etc.) you may need a larger set of transforms to handle the various set of special cases, and you may need to manufacture other code structures (e.g., temp variables of appropriate type). The good thing about a tool like DMS is that it can explicitly compute a symbolic type for an arbitrary expression (thus the type declaration of any needed temps) and that you can write such a larger set rather straightforwardly and apply all of them.

All that said, I'm not sure of the real value of doing your ternary-conditional-expression elimination operation. Once the compiler gets hold of the result, you may get similar object code as if you had not done the transformations at all. After all, the compiler can apply equivalence-preserving transformations, too.

There is obviously value in making regular changes in general, though.

(DMS can apply source-to-source program transformations to many langauges, including C, C++, Java, C# and PHP).

Ira Baxter