Gramática Lexical

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

Observação. 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 levar em conta o contexto sintactico. Isso ocorre devido ao fato de que XML e Visual Basic têm regras léxicos diferentes e o conjunto de regras léxicos em uso em qualquer momento específico depende de qual construção sintactica está sendo processada nesse momento. Essa especificação mantém esta seção de gramática lexical como um guia para as regras léxicos do código do Visual Basic regular.

LogicalLineStart
    : LogicalLine*
    ;

LogicalLine
    : LogicalLineElement* Comment? LineTerminator
    ;

LogicalLineElement
    : WhiteSpace
    | LineContinuation
    | Token
    ;

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

Caracteres e linhas

Os programas do Visual Basic são compostos por 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 de linha

Uma continuação de linha consiste em pelo menos um caractere de espaço em branco que imediatamente precede um único caractere 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 abrange mais de uma linha física. As continuações de linha são tratadas como se fossem espaço 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 sintactica permitem continuações de linha implícitas. Quando um terminador de linha é encontrado

  • após uma vírgula (,), parênteses abertos ((), chave aberta ({) ou expressão inserida aberta (<%=)

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

  • antes de um parêntese próximo ()), feche a chave (}) ou feche a expressão inserida (%>)

  • 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 não nível de arquivo

  • operadores de consulta antes e depois (Where, , SelectOrderetc.)

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

  • após operadores de atribuição (=, , :=, +=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

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. Essa última restrição é necessária porque o texto em blocos de compilação condicional que não são compilados não precisa ser sintaticamente válido. Assim, o texto no bloco pode acidentalmente ser "captado" pela instrução de compilação condicional, especialmente à medida que o idioma é estendido no futuro.)

Espaço em branco

O espaço em branco serve apenas para separar tokens e, de outra forma, é ignorado. Linhas lógicas que contêm apenas espaço em branco são ignoradas. (Observação. Terminadores de linha não são considerados espaço em branco.)

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

Comentários

Um comentário começa com um caractere de aspa única ou a palavra-chave REM. Um caractere de aspa única é um caractere de aspa única ASCII, um caractere unicode de aspa única esquerda ou um caractere unicode de aspa única direita. Os comentários podem começar em qualquer lugar em uma linha de origem e o final da linha física encerra 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 Padrão Unicode 15 com uma exceção: os identificadores podem começar com um caractere sublinhado (conector). Se um identificador começar com um sublinhado, ele deverá conter pelo menos um outro caractere de identificador válido para desambiguar 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 ou identificadores com escape 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 nomeado 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, portanto, dois identificadores serão considerados o mesmo identificador se forem diferentes apenas no caso. (Observação. Os mapeamentos de caso unicode standard um para um são usados ao comparar identificadores e todos os mapeamentos de caso específicos de localidade são ignorados.)

Tipo de caracteres

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 deverá concordar com o tipo especificado na própria declaração; caso contrário, ocorrerá um erro de 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á substituído implicitamente como o tipo da declaração.

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

Acrescentar 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 de 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, pois pode ser usado como um caractere de tipo e como separador no idioma. Para remover a ambiguidade, um ! caractere é um caractere de tipo, desde que o caractere a seguir não possa iniciar um identificador. Se puder, o ! caractere será 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 podem não ser usadas como identificadores, a menos que os identificadores sejam escapados. (Observação,EndIf, GoSub, Lete VariantWend são mantidos como palavras-chave, embora não sejam mais usados 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 valor específico de um tipo. Os tipos literais incluem booliano, inteiro, ponto flutuante, cadeia de caracteres, caractere e data.

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

Literais boolianos

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

BooleanLiteral
    : 'True' | 'False'
    ;

Literais inteiros

Literais inteiros podem ser decimal (base 10), hexadecimal (base 16) ou octal (base 8). Um literal inteiro decimal é uma cadeia de caracteres de dígitos decimais (0-9). Um literal hexadecimal é &H seguido por uma cadeia de caracteres de dígitos hexadecimal (0-9, A-F). Um literal octal é &O seguido por uma cadeia de caracteres de dígitos octais (0-7). Literais decimais representam diretamente o valor decimal do literal integral, enquanto literais octais e hexadecimal 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 literal é determinado por seu valor ou pelo caractere de tipo a seguir. Se nenhum caractere de tipo for especificado, os valores no intervalo do Integer tipo serão digitados como Integer; os valores fora do intervalo serão Integer digitados como Long. Se o tipo de um literal inteiro for de tamanho insuficiente para manter o literal inteiro, um erro de tempo de compilação resultará. (Observação. Não há um tipo de caractere porque Byte o caractere mais natural seria B, que é um caractere legal em um literal hexadecimal.)

Literais Floating-Point

Um literal de ponto flutuante é um literal inteiro seguido por um ponto decimal opcional (o caractere de período ASCII) e mantissa, e um expoente base 10 opcional. Por padrão, um literal de ponto flutuante é do tipo Double. Se o Singlecaractere , Doubleou Decimal tipo 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 de tempo de compilação resultará.

Observação. Vale a pena observar que o Decimal tipo de dados pode codificar zeros à direita em um valor. No momento, a especificação não faz comentários sobre se zeros à direita em um Decimal literal devem ser respeitados 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 cadeia de caracteres

Um literal de cadeia de caracteres é uma sequência de zero ou mais caracteres Unicode começando e terminando com um caractere aspas duplas ASCII, um caractere de aspa dupla unicode esquerdo ou um caractere de aspas duplas unicode direita. 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 necessariamente resulta em uma nova instância de cadeia de caracteres. Quando dois ou mais literais de cadeia de caracteres equivalentes de acordo com o operador de igualdade de cadeia de caracteres usando semântica de comparação binária aparecem no mesmo programa, esses literais de cadeia de caracteres podem se referir à mesma instâ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 instâ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 são 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 momento específico 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, será assumido 1º de janeiro do ano 1 no calendário gregoriano. Se o valor de hora for omitido, 12:00:00 AM será assumida.

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 primeiro século AD/CE, zeros à esquerda devem ser especificados.

Um valor de hora pode ser especificado usando um valor de 24 horas ou um valor de 12 horas; valores de tempo que omitem ou AMPM 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 os minutos e o segundo forem omitidos, AM ou PM precisarão ser especificados. Se o valor de data especificado estiver fora do intervalo do Date tipo, ocorrerá um erro de tempo de 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 em um tipo específico, ele é 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
    : '&' | '*' | '+' | '-' | '/' | '\\' | '^' | '<' | '=' | '>'
    ;