Direttive di pre-elaborazione

Dopo l'analisi lessicale di un file, si verificano diversi tipi di pre-elaborazione di origine. La compilazione condizionale più importante determina quale origine viene elaborata dalla grammatica sintattica; due altri tipi di direttive, ovvero direttive di origine esterna e direttive di area, forniscono meta-informazioni sull'origine, ma non hanno alcun effetto sulla compilazione.

Compilazione condizionale

La compilazione condizionale controlla se le sequenze di righe logiche vengono convertite in codice effettivo. All'inizio della compilazione condizionale, tutte le righe logiche sono abilitate; Tuttavia, racchiudere le righe nelle istruzioni di compilazione condizionale può disabilitare selettivamente tali righe all'interno del file, causandone l'ignorare durante il resto del processo di compilazione.

CCStart
    : CCStatement*
    ;

CCStatement
    : CCConstantDeclaration
    | CCIfGroup
    | LogicalLine
    ;

CCExpression
    : LiteralExpression
    | CCParenthesizedExpression
    | CCSimpleNameExpression
    | CCCastExpression
    | CCOperatorExpression
    | CCConditionalExpression
    ;

CCParenthesizedExpression
    : '(' CCExpression ')'
    ;

CCSimpleNameExpression
    : Identifier
    ;

CCCastExpression
    : 'DirectCast' '(' CCExpression ',' TypeName ')'
    | 'TryCast' '(' CCExpression ',' TypeName ')'
    | 'CType' '(' CCExpression ',' TypeName ')'
    | CastTarget '(' CCExpression ')'
    ;

CCOperatorExpression
    : CCUnaryOperator CCExpression
    | CCExpression CCBinaryOperator CCExpression
    ;

CCUnaryOperator
    : '+' | '-' | 'Not'
    ;

CCBinaryOperator
    : '+'     | '-'     | '*'   | '/'       | '\\'     | 'Mod' | '^' | '='
    | '<' '>' | '<'     | '>'   | '<' '='   | '>' '='  | '&'
    | 'And'   | 'Or'    | 'Xor' | 'AndAlso' | 'OrElse'
    | '<' '<' | '>' '>'
    ;

CCConditionalExpression
    : 'If' '(' CCExpression ',' CCExpression ',' CCExpression ')'
    | 'If' '(' CCExpression ',' CCExpression ')'
    ;

Ad esempio, il programma

#Const A = True
#Const B = False

Class C

#If A Then
    Sub F()
    End Sub
#Else
    Sub G()
    End Sub
#End If

#If B Then
    Sub H()
    End Sub
#Else
    Sub I()
    End Sub
#End If

End Class

produce la stessa sequenza di token del programma

Class C
    Sub F()
    End Sub

    Sub I()
    End Sub
End Class

Le espressioni costanti consentite nelle direttive di compilazione condizionale sono un subset di espressioni costanti generali.

Il preprocessore consente spazi vuoti e continuazioni di riga esplicite prima e dopo ogni token.

Direttive costanti condizionali

Le istruzioni costanti condizionali definiscono costanti presenti in uno spazio di dichiarazione di compilazione condizionale separato con ambito nel file di origine.

CCConstantDeclaration
    : '#' 'Const' Identifier '=' CCExpression LineTerminator
    ;

Lo spazio di dichiarazione è speciale in quanto non è necessaria alcuna dichiarazione esplicita delle costanti di compilazione condizionale. Le costanti condizionali possono essere definite in modo implicito in una direttiva di compilazione condizionale.

Prima di assegnare un valore, una costante di compilazione condizionale ha il valore Nothing. Quando a una costante di compilazione condizionale viene assegnato un valore, che deve essere un'espressione costante, il tipo della costante diventa il tipo del valore assegnato. Una costante di compilazione condizionale può essere ridefinita più volte in un file di origine.

Ad esempio, il codice seguente stampa solo la stringa about to print value e il valore di Test.

Module M1
    Sub PrintValue(Test As Integer)

#Const DebugCode = True

#If DebugCode Then
        Console.WriteLine("about to print value")
#End If

#Const DebugCode = False

        Console.WriteLine(Test)

#If DebugCode Then
        Console.WriteLine("printed value")
#End If

    End Sub
End Module

L'ambiente di compilazione può anche definire costanti condizionali in uno spazio di dichiarazione di compilazione condizionale.

Direttive di compilazione condizionale

Le direttive di compilazione condizionale controllano la compilazione condizionale.

CCIfGroup
    : '#' 'If' CCExpression 'Then'? LineTerminator CCStatement*
    CCElseIfGroup* CCElseGroup? '#' 'End' 'If' LineTerminator
    ;

CCElseIfGroup
    : '#' ElseIf CCExpression 'Then'? LineTerminator CCStatement*
    ;

CCElseGroup
    : '#' 'Else' LineTerminator CCStatement*
    ;

Le costanti condizionali possono fare riferimento solo a espressioni costanti e costanti di compilazione condizionale. Ognuna delle espressioni costanti all'interno di un singolo gruppo di compilazione condizionale viene valutata e convertita nel Boolean tipo in ordine testuale dal primo all'ultimo fino a quando una delle espressioni condizionali restituisce True. Se un'espressione non è convertibile in Boolean, viene restituito un errore in fase di compilazione. La semantica permissiva e i confronti di stringhe binarie vengono sempre usati durante la valutazione delle espressioni costanti di compilazione condizionale, indipendentemente dalle direttive o dalle impostazioni dell'ambiente di Option compilazione.

Tutte le righe racchiuse dal gruppo, incluse le direttive di compilazione condizionale annidate, sono disabilitate, ad eccezione delle righe tra l'istruzione contenente l'espressione True e l'istruzione condizionale successiva del gruppo o le righe tra l'istruzione Else e l'istruzione End If se Else viene visualizzato nel gruppo e tutte le espressioni restituiscono False.

In questo esempio la chiamata a WriteToLog nella direttiva di Trace compilazione condizionale non viene elaborata perché la direttiva di compilazione condizionale circostante Debug restituisce False.

#Const Debug = False   ' Debugging off
#Const Trace = True    ' Tracing on

Class PurchaseTransaction
    Sub Commit()

#If Debug Then
        CheckConsistency()
#If Trace Then
        WriteToLog(Me.ToString())
#End If
#End If
        ...
    End Sub
End Class

Direttive di origine esterna

Un file di origine può includere direttive di origine esterne che indicano un mapping tra le righe di origine e il testo esterno all'origine.

ESDStart
    : ExternalSourceStatement*
    ;

ExternalSourceStatement
    : ExternalSourceGroup
    | LogicalLine
    ;

ExternalSourceGroup
    : '#' 'ExternalSource' '(' StringLiteral ',' IntLiteral ')' LineTerminator
      LogicalLine* '#' 'End' 'ExternalSource' LineTerminator
    ;

Le direttive di origine esterna non hanno alcun effetto sulla compilazione e potrebbero non essere annidate. Per esempio:

Module Test
    Sub Main()

#ExternalSource("c:\wwwroot\inetpub\test.aspx", 30)
        Console.WriteLine("In test.aspx")
#End ExternalSource

    End Sub
End Module

Direttive region

Le direttive region raggruppano righe di codice sorgente, ma non hanno altro effetto sulla compilazione. L'intero gruppo può essere compresso e nascosto o espanso e visualizzato nell'ambiente di sviluppo integrato (IDE).

RegionStart
    : RegionStatement*
    ;

RegionStatement
    : RegionGroup
    | LogicalLine
    ;

RegionGroup
    : '#' 'Region' StringLiteral LineTerminator
      RegionStatement*
      '#' 'End' 'Region' LineTerminator
    ;

Le aree possono essere annidate. Le direttive region sono speciali in quanto non possono né iniziare né terminare all'interno di un corpo del metodo e devono rispettare la struttura del blocco del programma. Per esempio:

Module Test
#Region "Startup code - do not edit"
    Sub Main()
    End Sub
#End Region

End Module


' Error due to Region directives breaking the block structure
Class C
#Region "Fred"
End Class
#End Region

Direttive checksum esterne

Un file di origine può includere una direttiva checksum esterna che indica quale checksum deve essere generato per un file a cui viene fatto riferimento in una direttiva di origine esterna. In tutti gli altri aspetti, le direttive di origine esterna non hanno alcun effetto sulla compilazione.

ExternalChecksumStart
    : ExternalChecksumStatement*
    ;

ExternalChecksumStatement
    : '#' 'ExternalChecksum' '('
      StringLiteral ',' StringLiteral ',' StringLiteral
      ')' LineTerminator
    ;

Una direttiva checksum esterna contiene il nome file del file esterno, un identificatore univoco globale (GUID) associato al file e il checksum per il file. Il GUID viene specificato come costante stringa del formato "{xxxxxxxx-xxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}", dove x è una cifra esadecimale. Il checksum viene specificato come costante stringa del formato "xxxx...", dove x è una cifra esadecimale. Il numero di cifre in un checksum deve essere un numero pari.

A un file esterno possono essere associate più direttive checksum esterne, a condizione che tutti i valori GUID e checksum corrispondano esattamente. Se il nome del file esterno corrisponde al nome di un file compilato, il checksum viene ignorato a favore del calcolo del checksum del compilatore.

Per esempio:

#ExternalChecksum("c:\wwwroot\inetpub\test.aspx", _
    "{12345678-1234-1234-1234-123456789abc}", _
    "1a2b3c4e5f617239a49b9a9c0391849d34950f923fab9484")

Module Test
    Sub Main()

#ExternalSource("c:\wwwroot\inetpub\test.aspx", 30)
        Console.WriteLine("In test.aspx")
#End ExternalSource

    End Sub
End Module