patro
patro

Reputation: 121

Why are macros based on abstract syntax trees better than macros based on string preprocessing?

I am beginning my journey of learning Rust. I came across this line in Rust by Example:

However, unlike macros in C and other languages, Rust macros are expanded into abstract syntax trees, rather than string preprocessing, so you don't get unexpected precedence bugs.

Why is an abstract syntax tree better than string preprocessing?

Upvotes: 3

Views: 895

Answers (2)

DK.
DK.

Reputation: 59135

If you have this in C:

#define X(A,B) A+B
int r = X(1,2) * 3;

The value of r will be 7, because the preprocessor expands it to 1+2 * 3, which is 1+(2*3).

In Rust, you would have:

macro_rules! X { ($a:expr,$b:expr) => { $a+$b } }
let r = X!(1,2) * 3;

This will evaluate to 9, because the compiler will interpret the expansion as (1+2)*3. This is because the compiler knows that the result of the macro is supposed to be a complete, self-contained expression.

That said, the C macro could also be defined like so:

#define X(A,B) ((A)+(B))

This would avoid any non-obvious evaluation problems, including the arguments themselves being reinterpreted due to context. However, when you're using a macro, you can never be sure whether or not the macro has correctly accounted for every possible way it could be used, so it's hard to tell what any given macro expansion will do.

By using AST nodes instead of text, Rust ensures this ambiguity can't happen.

Upvotes: 18

Some programmer dude
Some programmer dude

Reputation: 409442

A classic example using the C preprocessor is

#define MUL(a, b) a * b

// ...

int res = MUL(x + y, 5);

The use of the macro will expand to

int res = x + y * 5;

which is very far from the expected

int res = (x + y) * 5;

This happens because the C preprocessor really just does simple text-based substitutions, it's not really an integral part of the language itself. Preprocessing and parsing are two separate steps.

If the preprocessor instead parsed the macro like the rest of the compiler, which happens for languages where macros are part of the actual language syntax, this is no longer a problem as things like precedence (as mentioned) and associativity are taken into account.

Upvotes: 7

Related Questions