What changed
The biggest change in the v10 compiler is how ifdefs are parsed. In the past we had a preprocessor, a tokenizer and a parser. The preprocessor would be provided with a list of defines, remove all code that was {$IFDEF}'d out and hand that to the parser. The new compiler does this completely different: It tries to parse ifdefs as if they were regular code concepts like statements. The reason we did this is to allow for a big future feature (can’t talk about that one yet), and make shared files a LOT faster, since the file only has to be parsed once, not once per platform.
How it works
How it works very much depends on where the parser is. There’s a lot of recovery involved but the general rule is that ifdefs now have to be at the same level as their else/endif and have to encapsulate a full concept. For example, this is allowed:
{$IFDEF CLR}
method Test;
{$ENDIF}
This is not:
method {$IFDEF CLR}Test{$ELSE}Test2{$ENDIF};
To do above, it should look like:
{$IFDEF CLR}
method Test;
{$ELSE}
method Test2;
{$ENDIF}
Ifdefs are allowed around and inside types, but if started before a type, they have to end after it, not inside:
Not allowed:
{$IFDEF TEST}
type ABC = class
method Test;
{$ENDIF}
end;
Allowed:
{$IFDEF TEST}
type ABC = class
method Test;
end;
{$ENDIF}
and
type ABC = class
{$IFDEF TEST}
method Test;
{$ENDIF}
end;
Types
In any place that accepts a type references ifdef is allowed too, to create a type that refers to a different one:
var x: {$IFDEF CLR}System.String{$ELSEIF JAVA}java.lang.String{$ENDIF};
This becomes a System.String or java.lang.String for CLR, Java and an “Error Type” on any other platform (Error types fail when they are seen by the compiler).
Statements
For statements the same rules as above apply, ifdefs have to contain a balanced statement.
Not allowed:
begin
if a = 15 then begin {$IFDEF TEST}
Step1;
end else begin {$ENDIF}
Step2;
end;
Allowed:
begin
{$IFDEF TEST2}
if a = 15 then begin
{$IFDEF TEST}
Step1;
{$ENDIF}
end else begin
Step2;
end;
{$ENDIF}
Expressions
Ifdefs in expressions are only allowed at the start of any expression like:
MyMethod.DoCall({$IFDEF TEST}a{$ELSE}b{$ENDIF}.Name);
Attributes
Attributes can be ifdefd out in any way, as long as the ifdef captures the whole attribute and not part of it.
Methods
One thing that compiler now enforces (which I think will aid big time in cross platform development) is that the interface and implementation of a method has to be consistent in it’s ifdefs. The compiler will error if the ifdef expression doesn’t match:
{$IFDEF A}
type
MyClass = public class
public
{$IFDEF B}
method Test;
{$ENDIF}
end;
{$ENDIF}
...
{$IFDEF A and B}
method MyClass.Test;
begin
end;
{$ENDIF}
Here the ifdef around Test has to be “A and B” to match the combined ifdef of the type above.
Ifdefs around method modifiers are allowed (like {$IFDEF TEST}virtual;{$ENDIF}), but not for empty as that would conditionally break the link between the interface and implementation. Additionally the compiler enforces that the signature of a method in the interfaces matches the implementation exactly, not just logically.
Uses
For uses we have logic that parses pretty much anything you can throw at it.
Other cases
We’ve added recovery and lookahead/back code for a lot of special cases that we hit in our own code but did have to do a few smaller changes. We’d like to hear about bugs you find so we can see if they’re fixable in the new compiler.