Directivas de preprocesamiento

Una vez que se ha analizado un archivo léxicamente, se producen varios tipos de preprocesamiento de origen. La compilación condicional más importante determina qué origen procesa la gramática sintáctica; dos otros tipos de directivas : directivas de origen externo y directivas de región: proporcionan metadatos sobre el origen, pero no tienen ningún efecto en la compilación.

Compilación condicional

La compilación condicional controla si las secuencias de líneas lógicas se traducen en código real. Al principio de la compilación condicional, todas las líneas lógicas están habilitadas; Sin embargo, incluir líneas en instrucciones de compilación condicionales puede deshabilitar selectivamente esas líneas dentro del archivo, lo que hace que se omitan durante el resto del proceso de compilación.

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

Por ejemplo, el programa

#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

genera exactamente la misma secuencia de tokens que el programa.

Class C
    Sub F()
    End Sub

    Sub I()
    End Sub
End Class

Las expresiones constantes permitidas en las directivas de compilación condicional son un subconjunto de expresiones constantes generales.

El preprocesador permite las continuaciones de línea explícitas y espacios en blanco antes y después de cada token.

Directivas constantes condicionales

Las instrucciones constantes condicionales definen constantes que existen en un espacio de declaración de compilación condicional independiente con ámbito en el archivo de origen.

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

El espacio de declaración es especial en que no es necesaria ninguna declaración explícita de constantes de compilación condicional: las constantes condicionales se pueden definir implícitamente en una directiva de compilación condicional.

Antes de asignar un valor, una constante de compilación condicional tiene el valor Nothing. Cuando se asigna un valor a una constante de compilación condicional, que debe ser una expresión constante, el tipo de la constante se convierte en el tipo del valor que se le asigna. Una constante de compilación condicional se puede redefinir varias veces a lo largo de un archivo de origen.

Por ejemplo, el código siguiente imprime solo la cadena about to print value y el valor de 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

El entorno de compilación también puede definir constantes condicionales en un espacio de declaración de compilación condicional.

Directivas de compilación condicional

Las directivas de compilación condicional controlan la compilación condicional.

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

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

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

Las constantes condicionales solo pueden hacer referencia a expresiones constantes y constantes de compilación condicional. Cada una de las expresiones constantes dentro de un único grupo de compilación condicional se evalúa y se convierte en el Boolean tipo en orden textual de primero a último hasta que una de las expresiones condicionales se evalúa como True. Si una expresión no se puede convertir en Boolean, se produce un error en tiempo de compilación. La semántica permisiva y las comparaciones de cadenas binarias siempre se usan al evaluar expresiones constantes de compilación condicional, independientemente de las Option directivas o la configuración del entorno de compilación.

Todas las líneas incluidas en el grupo, incluidas las directivas de compilación condicional anidadas, se deshabilitan excepto las líneas entre la instrucción que contiene la True expresión y la siguiente instrucción condicional del grupo, o las líneas entre la Else instrucción y la End If instrucción si Else aparece en el grupo y todas las expresiones se evalúan como False.

En este ejemplo, la llamada a WriteToLog en la Trace directiva de compilación condicional no se procesa porque la directiva de compilación condicional circundante Debug se evalúa como 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

Directivas de origen externo

Un archivo de origen puede incluir directivas de origen externas que indican una asignación entre líneas de origen y texto externo al origen.

ESDStart
    : ExternalSourceStatement*
    ;

ExternalSourceStatement
    : ExternalSourceGroup
    | LogicalLine
    ;

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

Las directivas de origen externo no tienen ningún efecto en la compilación y es posible que no estén anidadas. Por ejemplo:

Module Test
    Sub Main()

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

    End Sub
End Module

Directivas de región

Las directivas region agrupan líneas de código fuente, pero no tienen ningún otro efecto en la compilación. Todo el grupo se puede contraer y ocultar, o expandir y ver, en el entorno de desarrollo integrado (IDE).

RegionStart
    : RegionStatement*
    ;

RegionStatement
    : RegionGroup
    | LogicalLine
    ;

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

Las regiones se pueden anidar. Las directivas de región son especiales en que no pueden iniciar ni finalizar dentro de un cuerpo del método, y deben respetar la estructura de bloques del programa. Por ejemplo:

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

Directivas de suma de comprobación externa

Un archivo de origen puede incluir una directiva de suma de comprobación externa que indica qué suma de comprobación se debe emitir para un archivo al que se hace referencia en una directiva de origen externo. En todos los demás aspectos, las directivas de origen externo no tienen ningún efecto en la compilación.

ExternalChecksumStart
    : ExternalChecksumStatement*
    ;

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

Una directiva de suma de comprobación externa contiene el nombre de archivo externo, un identificador único global (GUID) asociado al archivo y la suma de comprobación del archivo. El GUID se especifica como una constante de cadena de la forma "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}", donde x es un dígito hexadecimal. La suma de comprobación se especifica como una constante de cadena de la forma "xxxx...", donde x es un dígito hexadecimal. El número de dígitos de una suma de comprobación debe ser un número par.

Un archivo externo puede tener varias directivas de suma de comprobación externas asociadas, siempre que todos los valores GUID y de suma de comprobación coincidan exactamente. Si el nombre del archivo externo coincide con el nombre de un archivo que se está compilando, la suma de comprobación se omite en favor del cálculo de la suma de comprobación del compilador.

Por ejemplo:

#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