Gramática Lexical

A compilação de um programa Visual Basic primeiro envolve a tradução do fluxo bruto de caracteres Unicode em um conjunto ordenado de tokens lexicais. Como a linguagem Visual Basic não é de formato livre, o conjunto de tokens é dividido em uma série de linhas lógicas. Uma linha lógica se estende desde o início do fluxo ou um terminador de linha até o próximo terminador de linha que não é precedido por uma continuação de linha ou até o final do fluxo.

Nota. Com a introdução de expressões literais XML na versão 9.0 da linguagem, o Visual Basic não tem mais uma gramática lexical distinta no sentido de que o código do Visual Basic pode ser tokenizado sem considerar o contexto sintático. Isso se deve ao fato de que XML e Visual Basic têm regras lexicais diferentes e o conjunto de regras lexicais em uso em qualquer momento específico depende de qual construção sintática está sendo processada naquele momento. Esta especificação mantém esta seção de gramática lexical como um guia para as regras lexicais do código regular do Visual Basic.

LogicalLineStart
    : LogicalLine*
    ;

LogicalLine
    : LogicalLineElement* Comment? LineTerminator
    ;

LogicalLineElement
    : WhiteSpace
    | LineContinuation
    | Token
    ;

Token
    : Identifier
    | Keyword
    | Literal
    | Separator
    | Operator
    ;

Personagens e Linhas

Os programas do Visual Basic são compostos de caracteres do conjunto de caracteres Unicode.

Character:
    '<Any Unicode character except a LineTerminator>'
    ;

Terminadores de linha

Caracteres de quebra de linha Unicode separam linhas lógicas.

LineTerminator
    : '<Unicode 0x00D>'
    | '<Unicode 0x00A>'
    | '<CR>'
    | '<LF>'
    | '<Unicode 0x2028>'
    | '<Unicode 0x2029>'
    ;

Continuação da linha

Uma continuação de linha consiste em pelo menos um caractere de espaço em branco que precede imediatamente um único caractere de sublinhado como o último caractere (diferente de espaço em branco) em uma linha de texto. Uma continuação de linha permite que uma linha lógica se estenda por mais de uma linha física. As continuações de linha são tratadas como se fossem espaços em branco, mesmo que não sejam.

LineContinuation
    : WhiteSpace '_' WhiteSpace* LineTerminator
    ;

O programa a seguir mostra algumas continuações de linha:

Module Test
    Sub Print( _
        Param1 As Integer, _
        Param2 As Integer )

        If (Param1 < Param2) Or _
            (Param1 > Param2) Then
            Console.WriteLine("Not equal")
        End If
    End Function
End Module

Alguns lugares na gramática sintática permitem continuações de linha implícitas. Quando um terminador de linha é encontrado

  • após uma vírgula (,), parêntese aberto ((), chave encaracolado aberta ({) ou expressão incorporada aberta (<%=)

  • após um qualificador de membro (. ou .@...), desde que algo esteja sendo qualificado (ou seja, não esteja usando um contexto implícito With )

  • antes de um parêntese fechado ()), fechar chave encaracolado (}) ou fechar expressão incorporada (%>)

  • após um menor que (<) em um contexto de atributo

  • antes de um maior que (>) em um contexto de atributo

  • após um maior que (>) em um contexto de atributo que não seja no nível de arquivo

  • antes e depois dos operadores de consulta (Where, Order, Select, etc.)

  • após operadores binários (+, -, /, *, etc.) em um contexto de expressão

  • após a atribuição de operadores (=, :=, +=, -=, etc.) em qualquer contexto.

O terminador de linha é tratado como se fosse uma continuação de linha.

Comma
    : ',' LineTerminator?
    ;

Period
    : '.' LineTerminator?
    ;

OpenParenthesis
    : '(' LineTerminator?
    ;

CloseParenthesis
    : LineTerminator? ')'
    ;

OpenCurlyBrace
    : '{' LineTerminator?
    ;

CloseCurlyBrace
    : LineTerminator? '}'
    ;

Equals
    : '=' LineTerminator?
    ;

ColonEquals
    : ':' '=' LineTerminator?
    ;

Por exemplo, o exemplo anterior também pode ser escrito como:

Module Test
    Sub Print(
        Param1 As Integer,
        Param2 As Integer)

        If (Param1 < Param2) Or
            (Param1 > Param2) Then
            Console.WriteLine("Not equal")
        End If
    End Function
End Module

As continuações de linha implícitas só serão inferidas diretamente antes ou depois do token especificado. Eles não serão inferidos antes ou depois de uma continuação de linha. Por exemplo:

Dim y = 10
' Error: Expression expected for assignment to x
Dim x = _

y

As continuações de linha não serão inferidas em contextos de compilação condicional. (Observação. Esta última restrição é necessária porque o texto em blocos de compilação condicionais que não são compilados não precisam ser sintaticamente válidos. Assim, o texto no bloco pode acidentalmente ser "pego" pela instrução de compilação condicional, especialmente à medida que a linguagem é estendida no futuro.)

Espaço em branco

O espaço em branco serve apenas para separar tokens e é ignorado. As linhas lógicas que contêm apenas espaço em branco são ignoradas. (Observação. Os terminadores de linha não são considerados espaços em branco.)

WhiteSpace
    : '<Unicode class Zs>'
    | '<Unicode Tab 0x0009>'
    ;

Observações

Um comentário começa com um caractere de aspas simples ou com a palavra-chave REM. Um caractere de aspas simples é um caractere de aspas únicas ASCII, um caractere de aspa única à esquerda Unicode ou um caractere de aspa única à direita Unicode. Os comentários podem começar em qualquer lugar em uma linha de origem, e o final da linha física termina o comentário. O compilador ignora os caracteres entre o início do comentário e o terminador de linha. Consequentemente, os comentários não podem se estender por várias linhas usando continuações de linha.

Comment
    : CommentMarker Character*
    ;

CommentMarker
    : SingleQuoteCharacter
    | 'REM'
    ;

SingleQuoteCharacter
    : '\''
    | '<Unicode 0x2018>'
    | '<Unicode 0x2019>'
    ;

Identificadores

Um identificador é um nome. Os identificadores do Visual Basic estão em conformidade com o Anexo 15 do Padrão Unicode, com uma exceção: os identificadores podem começar com um caractere de sublinhado (conector). Se um identificador começar com um sublinhado, ele deverá conter pelo menos um outro caractere identificador válido para desambiguá-lo de uma continuação de linha.

Identifier
    : NonEscapedIdentifier TypeCharacter?
    | Keyword TypeCharacter
    | EscapedIdentifier
    ;

NonEscapedIdentifier
    : '<Any IdentifierName but not Keyword>'
    ;

EscapedIdentifier
    : '[' IdentifierName ']'
    ;

IdentifierName
    : IdentifierStart IdentifierCharacter*
    ;

IdentifierStart
    : AlphaCharacter
    | UnderscoreCharacter IdentifierCharacter
    ;

IdentifierCharacter
    : UnderscoreCharacter
    | AlphaCharacter
    | NumericCharacter
    | CombiningCharacter
    | FormattingCharacter
    ;

AlphaCharacter
    : '<Unicode classes Lu,Ll,Lt,Lm,Lo,Nl>'
    ;

NumericCharacter
    : '<Unicode decimal digit class Nd>'
    ;

CombiningCharacter
    : '<Unicode combining character classes Mn, Mc>'
    ;

FormattingCharacter
    : '<Unicode formatting character class Cf>'
    ;

UnderscoreCharacter
    : '<Unicode connection character class Pc>'
    ;

IdentifierOrKeyword
    : Identifier
    | Keyword
    ;

Identificadores regulares podem não corresponder a palavras-chave, mas identificadores com escape ou identificadores com um caractere de tipo podem. Um identificador com escape é um identificador delimitado por colchetes. Os identificadores com escape seguem as mesmas regras que os identificadores regulares, exceto que eles podem corresponder a palavras-chave e podem não ter caracteres de tipo.

Este exemplo define uma classe nomeada class com um método compartilhado chamado shared que usa um parâmetro chamado boolean e, em seguida, chama o método.

Class [class]
    Shared Sub [shared]([boolean] As Boolean)
        If [boolean] Then
            Console.WriteLine("true")
        Else
            Console.WriteLine("false")
        End If
    End Sub
End Class

Module [module]
    Sub Main()
        [class].[shared](True)
    End Sub
End Module

Os identificadores não diferenciam maiúsculas de minúsculas, pelo que dois identificadores são considerados como sendo o mesmo identificador se diferirem apenas em maiúsculas e minúsculas. (Observação. Os mapeamentos de caso um-para-um do Unicode Standard são usados ao comparar identificadores e quaisquer mapeamentos de caso específicos da localidade são ignorados.)

Caracteres de tipo

Um caractere de tipo indica o tipo do identificador anterior. O caractere de tipo não é considerado parte do identificador.

TypeCharacter
    : IntegerTypeCharacter
    | LongTypeCharacter
    | DecimalTypeCharacter
    | SingleTypeCharacter
    | DoubleTypeCharacter
    | StringTypeCharacter
    ;

IntegerTypeCharacter
    : '%'
    ;

LongTypeCharacter
    : '&'
    ;

DecimalTypeCharacter
    : '@'
    ;

SingleTypeCharacter
    : '!'
    ;

DoubleTypeCharacter
    : '#'
    ;

StringTypeCharacter
    : '$'
    ;

Se uma declaração incluir um caractere de tipo, o caractere de tipo deve concordar com o tipo especificado na própria declaração; caso contrário, ocorrerá um erro em tempo de compilação. Se a declaração omitir o tipo (por exemplo, se não especificar uma As cláusula), o caractere de tipo será implicitamente substituído como o tipo da declaração.

Nenhum espaço em branco pode vir entre um identificador e seu caractere de tipo. Não há caracteres de tipo para Byte, SByte, UShort, ShortUInteger , ou , devido ULongà falta de caracteres adequados.

Anexar um caractere de tipo a um identificador que conceitualmente não tem um tipo (por exemplo, um nome de namespace) ou a um identificador cujo tipo discorda do tipo do caractere de tipo causa um erro em tempo de compilação.

O exemplo a seguir mostra o uso de caracteres de tipo:

' The follow line will cause an error: standard modules have no type.
Module Test1#
End Module

Module Test2

    ' This function takes a Long parameter and returns a String.
    Function Func$(Param&)

        ' The following line causes an error because the type character
        ' conflicts with the declared type of Func and Param.
        Func# = CStr(Param@)

        ' The following line is valid.
        Func$ = CStr(Param&)
    End Function
End Module

O caractere ! de tipo apresenta um problema especial na medida em que pode ser usado como um caractere de tipo e como um separador na linguagem. Para remover a ambiguidade, um ! caractere é um caractere de tipo, desde que o caractere que o segue não possa iniciar um identificador. Se puder, então o ! caractere é um separador, não um caractere de tipo.

Palavras-chave

Uma palavra-chave é uma palavra que tem um significado especial em uma construção de linguagem. Todas as palavras-chave são reservadas pelo idioma e não podem ser usadas como identificadores, a menos que os identificadores sejam escapados. (Nota.EndIf, GoSub, Let, Variant, e Wend são mantidas como palavras-chave, embora não sejam mais usadas no Visual Basic.)

Keyword
    : 'AddHandler'      | 'AddressOf'      | 'Alias'       | 'And'
    | 'AndAlso'         | 'As'             | 'Boolean'     | 'ByRef'
	| 'Byte'            | 'ByVal'          | 'Call'        | 'Case'        
	| 'Catch'           | 'CBool'          | 'CByte'       | 'CChar'       
	| 'CDate'           | 'CDbl'           | 'CDec'        | 'Char'        
	| 'CInt'            | 'Class'          | 'CLng'        | 'CObj'        
	| 'Const'           | 'Continue'       | 'CSByte'      | 'CShort'      
	| 'CSng'            | 'CStr'           | 'CType'       | 'CUInt'       
	| 'CULng'           | 'CUShort'        | 'Date'        | 'Decimal'     
	| 'Declare'         | 'Default'        | 'Delegate'    | 'Dim'         
	| 'DirectCast'      | 'Do'             | 'Double'      | 'Each'        
	| 'Else'            | 'ElseIf'         | 'End'         | 'EndIf'       
	| 'Enum'            | 'Erase'          | 'Error'       | 'Event'       
	| 'Exit'            | 'False'          | 'Finally'     | 'For'         
	| 'Friend'          | 'Function'       | 'Get'         | 'GetType'     
	| 'GetXmlNamespace' | 'Global'         | 'GoSub'       | 'GoTo'        
	| 'Handles'         | 'If'             | 'Implements'  | 'Imports'     
	| 'In'              | 'Inherits'       | 'Integer'     | 'Interface'   
	| 'Is'              | 'IsNot'          | 'Let'         | 'Lib'         
	| 'Like'            | 'Long'           | 'Loop'        | 'Me'          
	| 'Mod'             | 'Module'         | 'MustInherit' | 'MustOverride'
	| 'MyBase'          | 'MyClass'        | 'Namespace'   | 'Narrowing'   
	| 'New'             | 'Next'           | 'Not'         | 'Nothing'     
	| 'NotInheritable'  | 'NotOverridable' | 'Object'      | 'Of'          
	| 'On'              | 'Operator'       | 'Option'      | 'Optional'    
	| 'Or'              | 'OrElse'         | 'Overloads'   | 'Overridable' 
	| 'Overrides'       | 'ParamArray'     | 'Partial'     | 'Private'     
	| 'Property'        | 'Protected'      | 'Public'      | 'RaiseEvent'  
	| 'ReadOnly'        | 'ReDim'          | 'REM'         | 'RemoveHandler'
	| 'Resume'          | 'Return'         | 'SByte'       | 'Select'      
	| 'Set'             | 'Shadows'        | 'Shared'      | 'Short'       
	| 'Single'          | 'Static'         | 'Step'        | 'Stop'        
	| 'String'          | 'Structure'      | 'Sub'         | 'SyncLock'    
	| 'Then'            | 'Throw'          | 'To'          | 'True'        
	| 'Try'             | 'TryCast'        | 'TypeOf'      | 'UInteger'    
	| 'ULong'           | 'UShort'         | 'Using'       | 'Variant'     
	| 'Wend'            | 'When'           | 'While'       | 'Widening'    
	| 'With'            | 'WithEvents'     | 'WriteOnly'   | 'Xor'         
    ;

Literais

Um literal é uma representação textual de um determinado valor de um tipo. Os tipos literais incluem booleano, inteiro, ponto flutuante, string, caractere e data.

Literal
    : BooleanLiteral
    | IntegerLiteral
    | FloatingPointLiteral
    | StringLiteral
    | CharacterLiteral
    | DateLiteral
    | Nothing
    ;

Literais Booleanos

True e False são literais do Boolean tipo que mapeiam para o estado verdadeiro e falso, respectivamente.

BooleanLiteral
    : 'True' | 'False'
    ;

Literais inteiros

Os literais inteiros podem ser decimais (base 10), hexadecimais (base 16) ou octais (base 8). Um literal inteiro decimal é uma cadeia de dígitos decimais (0-9). Um literal hexadecimal é &H seguido por uma cadeia de dígitos hexadecimais (0-9, A-F). Um literal octal é &O seguido por uma sequência de dígitos octais (0-7). Os literais decimais representam diretamente o valor decimal do literal integral, enquanto os literais octais e hexadecimais representam o valor binário do literal inteiro (portanto, &H8000S é -32768, não um erro de estouro).

IntegerLiteral
    : IntegralLiteralValue IntegralTypeCharacter?
    ;

IntegralLiteralValue
    : IntLiteral
    | HexLiteral
    | OctalLiteral
    ;

IntegralTypeCharacter
    : ShortCharacter
    | UnsignedShortCharacter
    | IntegerCharacter
    | UnsignedIntegerCharacter
    | LongCharacter
    | UnsignedLongCharacter
    | IntegerTypeCharacter
    | LongTypeCharacter
    ;

ShortCharacter
    : 'S'
    ;

UnsignedShortCharacter
    : 'US'
    ;

IntegerCharacter
    : 'I'
    ;

UnsignedIntegerCharacter
    : 'UI'
    ;

LongCharacter
    : 'L'
    ;

UnsignedLongCharacter
    : 'UL'
    ;

IntLiteral
    : Digit+
    ;

HexLiteral
    : '&' 'H' HexDigit+
    ;

OctalLiteral
    : '&' 'O' OctalDigit+
    ;

Digit
    : '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
    ;

HexDigit
    : '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
    | 'A' | 'B' | 'C' | 'D' | 'E' | 'F'
    ;

OctalDigit
    : '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7'
    ;

O tipo de um literal é determinado pelo seu valor ou pelo seguinte caractere de tipo. Se nenhum caractere de tipo for especificado, os valores no intervalo do Integer tipo serão digitados como Integer; valores fora do intervalo para Integer serão digitados como Long. Se o tipo de um literal inteiro for de tamanho insuficiente para manter o literal inteiro, um erro em tempo de compilação resultará. (Observação. Não há um caractere de tipo para Byte porque o caractere mais natural seria B, que é um caractere legal em um literal hexadecimal.)

Floating-Point Literais

Um literal de vírgula flutuante é um literal inteiro seguido por um ponto decimal opcional (o caractere de período ASCII) e mantissa, e um expoente de base 10 opcional. Por padrão, um literal de ponto flutuante é do tipo Double. Se o Singlecaractere , Doubleou Decimal type for especificado, o literal será desse tipo. Se o tipo de um literal de ponto flutuante for de tamanho insuficiente para manter o literal de ponto flutuante, um erro em tempo de compilação resultará.

Nota. Vale a pena notar que o Decimal tipo de dados pode codificar zeros à direita em um valor. A especificação atualmente não faz nenhum comentário sobre se zeros à direita em um Decimal literal devem ser honrados por um compilador.

FloatingPointLiteral
    : FloatingPointLiteralValue FloatingPointTypeCharacter?
    | IntLiteral FloatingPointTypeCharacter
    ;

FloatingPointTypeCharacter
    : SingleCharacter
    | DoubleCharacter
    | DecimalCharacter
    | SingleTypeCharacter
    | DoubleTypeCharacter
    | DecimalTypeCharacter
    ;

SingleCharacter
    : 'F'
    ;

DoubleCharacter
    : 'R'
    ;

DecimalCharacter
    : 'D'
    ;

FloatingPointLiteralValue
    : IntLiteral '.' IntLiteral Exponent?
    | '.' IntLiteral Exponent?
    | IntLiteral Exponent
    ;

Exponent
    : 'E' Sign? IntLiteral
    ;

Sign
    : '+'
    | '-'
    ;

Literais de strings

Um literal de cadeia de caracteres é uma sequência de zero ou mais caracteres Unicode que começa e termina com um caractere de aspas duplas ASCII, um caractere de aspas duplas à esquerda Unicode ou um caractere de aspas duplas à direita Unicode. Dentro de uma cadeia de caracteres, uma sequência de dois caracteres de aspas duplas é uma sequência de escape que representa uma aspa dupla na cadeia de caracteres.

StringLiteral
    : DoubleQuoteCharacter StringCharacter* DoubleQuoteCharacter
    ;

DoubleQuoteCharacter
    : '"'
    | '<unicode left double-quote 0x201c>'
    | '<unicode right double-quote 0x201D>'
    ;

StringCharacter
    : '<Any character except DoubleQuoteCharacter>'
    | DoubleQuoteCharacter DoubleQuoteCharacter
    ;

Uma constante de cadeia de caracteres é do String tipo.

Module Test
    Sub Main()

        ' This prints out: ".
        Console.WriteLine("""")

        ' This prints out: a"b.
        Console.WriteLine("a""b")

        ' This causes a compile error due to mismatched double-quotes.
        Console.WriteLine("a"b")
    End Sub
End Module

O compilador tem permissão para substituir uma expressão de cadeia de caracteres constante por um literal de cadeia de caracteres. Cada literal de cadeia de caracteres não resulta necessariamente em uma nova ocorrência de cadeia de caracteres. Quando dois ou mais literais de cadeia de caracteres que são equivalentes de acordo com o operador de igualdade de cadeia usando semântica de comparação binária aparecem no mesmo programa, esses literais de cadeia de caracteres podem se referir à mesma ocorrência de cadeia de caracteres. Por exemplo, a saída do programa a seguir pode retornar True porque os dois literais podem se referir à mesma ocorrência de cadeia de caracteres.

Module Test
    Sub Main()
        Dim a As Object = "he" & "llo"
        Dim b As Object = "hello"
        Console.WriteLine(a Is b)
    End Sub
End Module

Literais de caracteres

Um literal de caractere representa um único caractere Unicode do Char tipo. Dois caracteres de aspas duplas é uma sequência de escape que representa o caractere de aspas duplas.

CharacterLiteral
    : DoubleQuoteCharacter StringCharacter DoubleQuoteCharacter 'C'
    ;
Module Test
    Sub Main()

        ' This prints out: a.
        Console.WriteLine("a"c)

        ' This prints out: ".
        Console.WriteLine(""""c)
    End Sub
End Module

Literais de data

Um literal de data representa um determinado momento no tempo expresso como um valor do Date tipo.

DateLiteral
    : '#' WhiteSpace* DateOrTime WhiteSpace* '#'
    ;

DateOrTime
    : DateValue WhiteSpace+ TimeValue
    | DateValue
    | TimeValue
    ;

DateValue
    : MonthValue '/' DayValue '/' YearValue
    | MonthValue '-' DayValue '-' YearValue
    ;

TimeValue
    : HourValue ':' MinuteValue ( ':' SecondValue )? WhiteSpace* AMPM?
    | HourValue WhiteSpace* AMPM
    ;

MonthValue
    : IntLiteral
    ;

DayValue
    : IntLiteral
    ;

YearValue
    : IntLiteral
    ;

HourValue
    : IntLiteral
    ;

MinuteValue
    : IntLiteral
    ;

SecondValue
    : IntLiteral
    ;

AMPM
    : 'AM' | 'PM'
    ;    

O literal pode especificar uma data e uma hora, apenas uma data ou apenas uma hora. Se o valor da data for omitido, então 1 de janeiro do ano 1 no calendário gregoriano é assumido. Se o valor de tempo for omitido, então 12:00:00 AM é assumido.

Para evitar problemas com a interpretação do valor do ano em um valor de data, o valor do ano não pode ser de dois dígitos. Ao expressar uma data no século I d.C./CE, zeros à esquerda devem ser especificados.

Um valor de tempo pode ser especificado usando um valor de 24 horas ou um valor de 12 horas; valores de tempo que omitem um AM ou PM são considerados valores de 24 horas. Se um valor de tempo omitir os minutos, o literal 0 será usado por padrão. Se um valor de tempo omitir os segundos, o literal 0 será usado por padrão. Se ambos os minutos e segundos forem omitidos, então AM ou PM devem ser especificados. Se o valor de data especificado estiver fora do intervalo do tipo, ocorrerá um erro em tempo de Date compilação.

O exemplo a seguir contém vários literais de data.

Dim d As Date

d = # 8/23/1970 3:45:39AM #
d = # 8/23/1970 #              ' Date value: 8/23/1970 12:00:00AM.
d = # 3:45:39AM #              ' Date value: 1/1/1 3:45:39AM.
d = # 3:45:39 #                ' Date value: 1/1/1 3:45:39AM.
d = # 13:45:39 #               ' Date value: 1/1/1 1:45:39PM.
d = # 1AM #                    ' Date value: 1/1/1 1:00:00AM.
d = # 13:45:39PM #             ' This date value is not valid.

Nada

Nothing é um literal especial; Ele não tem um tipo e é conversível para todos os tipos no sistema de tipos, incluindo parâmetros de tipo. Quando convertido para um tipo específico, é o equivalente ao valor padrão desse tipo.

Nothing
    : 'Nothing'
    ;

Separadores

Os seguintes caracteres ASCII são separadores:

Separator
    : '(' | ')' | '{' | '}' | '!' | '#' | ',' | '.' | ':' | '?'
    ;

Caracteres do operador

Os seguintes caracteres ASCII ou sequências de caracteres denotam operadores:

Operator
    : '&' | '*' | '+' | '-' | '/' | '\\' | '^' | '<' | '=' | '>'
    ;