Visual Basic is not free-format: the compiler first cuts the text into logical lines, and a logical line is a statement. Since XML literals arrived in version 9 the lexical rules also depend on the syntax around them (XML text is read with different rules from code), so the grammar below is a guide to the lexical rules of ordinary code. It follows the language specification and the scanner of Roslyn.
Lines, white space and comments
- Line terminators: CR, LF, CR LF, U+2028 and U+2029 (Roslyn also accepts U+0085). A logical line runs from one terminator to the next that is not preceded by a line continuation.
- White space: the tab and the characters of Unicode class Zs. It only separates tokens. A logical line with only white space is ignored.
- Comments start with an apostrophe (the ASCII
', or the curly‘or’) or with the wordREMfollowed by a space or the end of the line, and run to the end of the physical line. There are no block comments, and a comment cannot continue with an underscore. A word that merely starts withrem(remark) is a name. - Documentation comments start with three apostrophes (
''') on the line before a declaration and hold XML (<summary>,<param name="…">,<returns>,<remarks>,<exception cref="…">) that tools turn into help text.
' a comment
REM another comment
Dim x = 1 ' a comment after code
''' <summary>Documents the declaration that follows.</summary>
''' <param name="n">The count.</param>
Sub Count(n As Integer)
End Sub
Line continuation
A line continuation is a space or tab, an underscore as the last character of the line (white space may follow it), and the line end; the next line then continues the same statement. Since version 16 a comment may follow the underscore. Since version 10 (Visual Studio 2010) the line break is also ignored where the grammar cannot end there anyway:
- after a comma, an opening bracket
(or{, an opening embedded expression<%=, a member qualifier (.,.@,...) when something is being qualified; - before a closing bracket
)or}, or%>; - after
<and before>of an attribute (and after>of an attribute that is not at file level); - before and after query operators (
Where,Order By,Select…), after binary operators in an expression, and after assignment operators (=,:=,+=…).
An implicit continuation is never inferred next to an explicit one (a blank line after _ ends the statement), and never in a conditional-compilation line.
Dim total = 1 +
2 +
3
Dim old = 1 + _
2
Dim list = New List(Of Integer) From {
1,
2
}
Dim q = From x In list
Where x > 1
Select x
Dim t = old + _ ' a comment may follow the underscore (version 16)
total
Identifiers and type characters
An identifier starts with a letter (Unicode classes Lu, Ll, Lt, Lm, Lo, Nl) or with an underscore that is followed by at least one more identifier character (a lone _ would be a line continuation). Later characters may also be digits, combining marks, formatting characters and connectors. Identifiers are not case-sensitive: Count, COUNT and count are the same name (the Unicode one-to-one case mappings are used).
An escaped identifier is a name in square brackets, [Class]; it may be a reserved word and may not carry a type character. A type character written straight after a name says its type, and the name may then be a keyword (Mid$).
| Character | Type | Character | Type |
|---|---|---|---|
% | Integer | ! | Single |
& | Long | # | Double |
@ | Decimal | $ | String |
No white space may come between the name and the character, and there are no type characters for Byte, SByte, Short, UShort, UInteger or ULong. A ! after a name is a type character only if the next character cannot start a name; otherwise it is the dictionary-lookup operator (row!Name).
Dim [Class] As Integer = 1 ' escaped keyword
Dim name$ = "text" ' type character $ means String
Dim count% = 2 ' % means Integer
Dim big& = 3 ' & means Long
Dim ratio# = 1.5 ' # means Double
Dim price@ = 2.5@ ' @ means Decimal
Dim ünï = 1 ' Unicode letters
Dim _under = 2
Keywords
The reserved words (they cannot be names unless escaped):
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
EndIf, GoSub, Let, Variant and Wend are kept from older versions and no longer do anything useful. Roslyn’s keyword table also lists NameOf (the operator that gives the name of a variable, type or member as a string, version 14); the viewer colours it as a keyword when an opening bracket follows.
The contextual keywords are keywords only in the place where they have a meaning, and ordinary names elsewhere:
| Words | Where they are keywords |
|---|---|
Aggregate From Group Join Order By Where Skip Take Distinct Into Ascending Descending Equals | inside a query expression (From and Aggregate where a query can start) |
Async Iterator | as a modifier of a method, property or lambda (Async Function, Iterator Sub) |
Await | as the operator in front of an expression, in an Async method or lambda |
Yield | as the statement in an Iterator method |
Explicit Strict Infer Compare Binary Text Off | after Option |
Ansi Unicode Auto Lib Alias | in a Declare statement (Lib and Alias are reserved) |
Preserve | after ReDim |
Custom | before Event |
Key | in an anonymous type initialiser (New With {Key .Id = 1}) |
Out | as variance in a type parameter list (Of Out T) |
IsTrue IsFalse | as the name of an operator (Operator IsTrue) |
Until | after Do or Loop |
Mid | as the Mid assignment statement |
Assembly Module | in an attribute target (<Assembly: …>) |
Region ExternalSource ExternalChecksum Disable Enable Warning Reference | after # in directives |
Literals
Dim a = 42 ' Integer
Dim b = &HFF_FFS ' hexadecimal Short
Dim c = &O777 ' octal
Dim d = &B1010_1010 ' binary, with a digit separator
Dim e = 1_000_000L ' Long
Dim f = 4000000000UI ' UInteger
Dim g = 5US ' UShort
Dim h = 6UL ' ULong
Dim i = 3.14 ' Double
Dim j = 3.14F ' Single
Dim k = 2.5R ' Double
Dim l = 1.5E-3 ' Double with an exponent
Dim m = 19.99D ' Decimal
Dim n = 19.99@ ' Decimal
Dim o = 7% ' Integer
Dim p = 8& ' Long
Dim q = 1.5! ' Single
Dim r = 1.5# ' Double
Dim s = "He said ""hi""" ' String with doubled quotes
Dim t = "x"c ' Char
Dim u = """"c ' the Char "
Dim v = #8/23/1970 3:45:39 AM# ' Date
Dim w = #2020-02-29# ' Date, year first
Dim x = #1:30 PM# ' Date (time only)
Dim y = True
Dim z As Object = Nothing
| Literal | Rules |
|---|---|
| Boolean, Nothing | True and False; Nothing has no type and converts to every type as its default value |
| Integer | decimal digits, &H hexadecimal, &O octal, &B binary (version 15). A based literal is a bit pattern (&H8000S is −32768). The type is Integer if the value fits, else Long, or the suffix: S Short, I Integer, L Long, US UShort, UI UInteger, UL ULong (any case), or the type characters % and &. There is no suffix for Byte (a B would be a hex digit) |
| Digit separators | an underscore between digits of any integer or floating literal (version 15); an underscore straight after the &H, &O or &B prefix (version 15.5); not at the end of the digits |
| Floating point | digits with . and digits, or a leading .5, and an optional exponent E with a sign: Double by default. Suffixes F or ! Single, R or # Double, D or @ Decimal. An integer followed by one of these is a floating literal (5F). The old D exponent (1D5) is an error |
| String | between double quotes (the curly “ and ” too); a doubled quote inside stands for one. Since version 14 a literal may run over several lines, which then hold line breaks. A string is never continued with an underscore |
| Char | one character between quotes followed by c or C: "a"c, and """"c for the quote. ""c and "ab"c are errors |
| Date | # date and/or time #: month/day/year (or month-day-year), or, since version 14, year first (#2020-02-29#); time as h:m, h:m:s with an optional AM or PM, or h AM. A literal with no date is January 1 of year 1; a year has four digits (leading zeros for the first century); a time without AM or PM is 24-hour |
| Interpolated string | $"…" with {expression} holes, {{ and }} for the braces, an alignment after a comma and a format after a colon: {n,3:D2} (version 14). A hole holds ordinary code, including strings; its colon ends the expression unless it is inside brackets |
Dim name = "Ada"
Dim n = 3
Dim s1 = $"Hello {name}, you have {n,3:D2} new {If(n = 1, "message", "messages")}"
Dim s2 = $"{{literal braces}} {name}"
Dim s3 = "first line
second line"
Separators and operators
The separators are ( ) { } ! # , . : ?; the operator characters are & * + - / \ ^ < = >, which combine into <> <= >= << >> := and the assignment operators &= *= += -= /= \= ^= <<= >>=. White space is allowed between the two characters of a compound assignment operator (x + = 1 reads as x += 1). See operators. The tokens ?., .@, ..., <%=, %>, $" and the attribute brackets are read in the contexts that give them meaning.
Not described
Known, not yet covered
- Reference: full-width forms (Roslyn converts full-width letters, digits, quotes,
&,$and the operators to their ordinary forms), Roslyn’s version-conflict markers (<<<<<<<,=======,>>>>>>>lines) and the exact error recovery of the scanner are not described. - Viewer: the viewer does not read full-width forms; a string that is never closed colours the rest of the file as a string, as the compiler reads it (a multi-line literal is legal since version 14).
See also
- XML literals – the lexical rules of XML inside code
- Types and variables – what the literals and type characters mean