diff --git a/vba/vba7_1/README.md b/vba/vba7_1/README.md new file mode 100644 index 0000000000..dcbc3fe160 --- /dev/null +++ b/vba/vba7_1/README.md @@ -0,0 +1,9 @@ +# Visual Basic 7.1 Grammar for ANTLR4 + +Derived from the Visual Basic 7.1 language reference + +https://officeprotocoldoc.z19.web.core.windows.net/files/MS-VBAL/%5bMS-VBAL%5d.pdf + +This grammar ignores conditional-compilation statements. The vba_cc grammar can be used against vba files to analyze that portion of the code. + +Line endings, whitespace, and comments are traditionally removed from parsers, but the VBA standard dictates when and how some of these are valid, so they remain. Unfortunately, this leaves open room for more inaccuracy. Please report any whitespace bugs. diff --git a/vba/vba7_1/desc.xml b/vba/vba7_1/desc.xml new file mode 100644 index 0000000000..c99935326b --- /dev/null +++ b/vba/vba7_1/desc.xml @@ -0,0 +1,5 @@ + + + ^4.10 + CSharp;Dart;Go;Java;JavaScript;Python3;TypeScript + diff --git a/vba/vba7_1/examples/Form1.frm b/vba/vba7_1/examples/Form1.frm new file mode 100644 index 0000000000..0183ff3268 --- /dev/null +++ b/vba/vba7_1/examples/Form1.frm @@ -0,0 +1,257 @@ +VERSION 5.00 +Object = "{F9043C88-F6F2-101A-A3C9-08002B2F49FB}#1.2#0"; "COMDLG32.OCX" +Begin VB.Form Form1 + BorderStyle = 1 'Fixed Single + Caption = "Virtually Mapped File Hex Viewer" + ClientHeight = 5070 + ClientLeft = 150 + ClientTop = 795 + ClientWidth = 10215 + LinkTopic = "Form1" + MaxButton = 0 'False + MinButton = 0 'False + ScaleHeight = 5070 + ScaleWidth = 10215 + StartUpPosition = 3 'Windows Default + Begin VB.VScrollBar VScroll1 + Height = 4815 + LargeChange = 20 + Left = 9855 + Max = 100 + Min = -32768 + TabIndex = 0 + TabStop = 0 'False + Top = 120 + Value = -32768 + Width = 255 + End + Begin VB.PictureBox Picture2 + Appearance = 0 'Flat + BackColor = &H80000005& + BeginProperty Font + Name = "Courier" + Size = 9.75 + Charset = 0 + Weight = 400 + Underline = 0 'False + Italic = 0 'False + Strikethrough = 0 'False + EndProperty + ForeColor = &H80000008& + Height = 4815 + Left = 120 + ScaleHeight = 4785 + ScaleWidth = 1065 + TabIndex = 2 + Top = 120 + Width = 1095 + End + Begin VB.PictureBox Picture1 + Appearance = 0 'Flat + BackColor = &H80000005& + BeginProperty Font + Name = "Courier" + Size = 9.75 + Charset = 0 + Weight = 400 + Underline = 0 'False + Italic = 0 'False + Strikethrough = 0 'False + EndProperty + ForeColor = &H80000008& + Height = 4815 + Left = 1200 + ScaleHeight = 4785 + ScaleWidth = 8625 + TabIndex = 1 + Top = 120 + Width = 8655 + End + Begin MSComDlg.CommonDialog CD + Left = 5760 + Top = 4560 + _ExtentX = 847 + _ExtentY = 847 + _Version = 393216 + End + Begin VB.Menu mnuFile + Caption = "&File" + Begin VB.Menu mnuFileOpen + Caption = "&Open" + End + End +End +Attribute VB_Name = "Form1" +Attribute VB_GlobalNameSpace = False +Attribute VB_Creatable = False +Attribute VB_PredeclaredId = True +Attribute VB_Exposed = False +'' +' This demonstrates techniques for using the MemoryMappedFile class +' and some additional classes and functions. +' +Option Explicit + +Private Const LINES_PER_PAGE As Long = 24 +Private Const BYTES_PER_LINE As Long = 32 + +Private mFile As MemoryMappedFile +Private mStream As MemoryMappedViewStream +Private mTopLine As Long +Private mPageBuilder As New StringBuilder +Private mLineNumberBuilder As New StringBuilder +Private mReader As BinaryReader + + +'' +' Lets load up the file, shall we? +Private Sub LoadFile(ByVal FileName As String) + ' Get the file mapped to memory and byte array accessible. + Call OpenFile(FileName) + + ' This will overflow on 2+ meg files because of the scrollbar limit. + ' We want the ceiling so we can count for any fractional portion. + ' + ' We offset it by -32768 to utilize the full range of the + ' limited scrollbar control. + Dim MaxLine As Long + + MaxLine = Ceiling(mStream.Length / BYTES_PER_LINE) - (32768 + LINES_PER_PAGE) + If MaxLine < -32768 Then MaxLine = -32768 + VScroll1.Max = MaxLine + VScroll1.Value = VScroll1.Min + + ' And update the display. + Call UpdateDisplay +End Sub + +'' +' Creates a new memory mapping of a file and makes it byte array accessible. +Private Sub OpenFile(ByVal FileName As String) + ' Be sure to release the previous file. + Call CloseFile + + ' Create our memory mapped file. + Set mFile = MemoryMappedFile.CreateFromFile(FileName) + + ' And retrieve a accessor for the file. + Set mStream = mFile.CreateViewStream + Set mReader = NewBinaryReader(mStream, LeaveOpen:=True) +End Sub + +'' +' Build up the text to be displayed in the two picture controls. +' This gives us a virtual view of the mapped file. +Private Sub DisplayPage() + ' Reset our builders to clear anything in them. + mPageBuilder.Length = 0 + mLineNumberBuilder.Length = 0 + + ' Where do we start in the byte array? + Dim Index As Long + Index = mTopLine * BYTES_PER_LINE + mStream.Position = Index + + Dim i As Long + For i = 1 To BYTES_PER_LINE * LINES_PER_PAGE Step 4 + ' We can't convert less than 4 bytes at a time, so + ' make sure we haven't run out of 4 byte chunks. + If Index + 4 > mStream.Length Then Exit For + + ' Add the line number for the current line only if + ' we are at the beginning of the line currently being built. + If Index Mod 32 = 0 Then Call mLineNumberBuilder.AppendFormat("{0:X8}" & vbCrLf, Index) + + ' Convert a 4 byte chunk to a vbLong and append + ' it to the text as a hex value with atleast 8 characters. +' Call mPageBuilder.AppendFormat("{0:X8} ", BitConverter.ToLong(mBytes, Index)) + Call mPageBuilder.AppendFormat("{0:X8}", mReader.ReadInt32) + + ' Move to the next 4 byte chunk. + Index = Index + 4 + + ' If we have reached the end of the line, then start a new line. + If Index Mod 32 = 0 Then Call mPageBuilder.AppendString(vbCrLf) + Next i + + ' Check if our index is within the last 4 bytes of the + ' end of the array. If so, we have to manually append + ' the remaining bytes manually, since we didn't have + ' a 4 byte chunk to append previously. + If Index <= mStream.Length And Index + 4 > mStream.Length Then + ' Loop through the remaining bytes backwards so + ' we can build up a final vbLong value. + For i = mStream.Length To Index Step -1 + mStream.Position = i + + Dim j As Long + j = j * &H100 + mStream.ReadByte + Next i + + ' Append the remaining byte values. + Call mPageBuilder.AppendFormat("{0:X8}", j) + End If + + ' Display the hex mapped values. + Picture1.Cls + Picture1.Print mPageBuilder.ToString + + ' Display the hex line numbers. + Picture2.Cls + Picture2.Print mLineNumberBuilder.ToString +End Sub + +'' +' Releases the byte array back to the mapped file and closes the file. +' +Private Sub CloseFile() + ' The byte array view is attached to a barrowed + ' view of the mapped file. We must give it back + ' or bad things can happen during teardown. +' If Not CorArray.IsNull(mBytes) Then +' Call mFile.DeleteView(mBytes) +' Call mFile.CloseFile +' End If + If Not mStream Is Nothing Then + mStream.CloseStream + Set mStream = Nothing + End If +End Sub + +'' +' Set the first line of the page to be displayed and display the page. +Private Sub UpdateDisplay() + mTopLine = VScroll1.Value + 32768 + Call DisplayPage +End Sub + +'' +' We are getting out of here. +' +Private Sub Form_Unload(Cancel As Integer) + ' We want to be sure to release the byte array + ' back to the mapped file before the variables + ' are deallocated by VB, or else bad things will happen. + + Call CloseFile +End Sub + +Private Sub mnuFileOpen_Click() + On Error GoTo errTrap + With CD + .CancelError = True + .DialogTitle = "Find File" + Call .ShowOpen + On Error GoTo 0 + Call LoadFile(.FileName) + End With +errTrap: +End Sub + +Private Sub VScroll1_Change() + Call UpdateDisplay +End Sub + +Private Sub VScroll1_Scroll() + Call UpdateDisplay +End Sub diff --git a/vba/vba7_1/examples/SQLHelperFunctions.bas b/vba/vba7_1/examples/SQLHelperFunctions.bas new file mode 100644 index 0000000000..0f8d488eb4 --- /dev/null +++ b/vba/vba7_1/examples/SQLHelperFunctions.bas @@ -0,0 +1,86 @@ +Attribute VB_Name = "SQLHelperFunctions" +Public Function toUnix(dt) As Long + toUnix = DateDiff("s", "1/1/1970", dt) +End Function + +Public Function toISO(dt) As String + toISO = Format(dt, "YYYY-MM-DD") & "T" & Format(dt, "HH:MM:SS") +End Function + +Public Function str(vValue) As String + str = "'" & Replace(vValue, "'", "''") & "'" +End Function + +Function JoinArrayofArrays(ByVal vArray As Variant, _ + Optional ByVal WordDelim As String = " ", _ + Optional ByVal LineDelim As String = vbNewLine) As String + Dim R As Long, Lines() As String + ReDim Lines(0 To UBound(vArray)) + For R = 0 To UBound(vArray) + Dim InnerArray() As Variant + InnerArray = vArray(R) + Lines(R) = Join(InnerArray, WordDelim) + Next + JoinArrayofArrays = Join(Lines, LineDelim) +End Function + +Function getDimension(Var As Variant) As Long + On Error GoTo Err + Dim i As Long + Dim tmp As Long + i = 0 + Do While True + i = i + 1 + tmp = UBound(Var, i) + Loop + + Err: + getDimension = i - 1 +End Function + +Public Sub QuickSort(vArray As Variant, inLow As Long, inHi As Long) + Dim pivot As Variant + Dim tmpSwap As Variant + Dim tmpLow As Long + Dim tmpHi As Long + + tmpLow = inLow + tmpHi = inHi + + pivot = vArray((inLow + inHi) \ 2) + + While (tmpLow <= tmpHi) + + While (vArray(tmpLow) < pivot And tmpLow < inHi) + tmpLow = tmpLow + 1 + Wend + + While (pivot < vArray(tmpHi) And tmpHi > inLow) + tmpHi = tmpHi - 1 + Wend + + If (tmpLow <= tmpHi) Then + tmpSwap = vArray(tmpLow) + vArray(tmpLow) = vArray(tmpHi) + vArray(tmpHi) = tmpSwap + tmpLow = tmpLow + 1 + tmpHi = tmpHi - 1 + End If + Wend + If (inLow < tmpHi) Then + QuickSort vArray, inLow, tmpHi + End If + If (tmpLow < inHi) Then + QuickSort vArray, tmpLow, inHi + End If +End Sub + +Public Function ArrayPush(vArray As Variant, newValue As Variant) + ArrLen = UBound(vArray) + If IsEmpty(vArray(0)) Then + ArrLen = -1 + End If + ReDim Preserve vArray(0 To ArrLen + 1) + vArray(ArrLen + 1) = newValue + ArrayPush = vArray +End Function diff --git a/vba/vba7_1/examples/SQLSelect.cls b/vba/vba7_1/examples/SQLSelect.cls new file mode 100644 index 0000000000..45b97cca1e --- /dev/null +++ b/vba/vba7_1/examples/SQLSelect.cls @@ -0,0 +1,291 @@ +VERSION 1.0 CLASS +BEGIN + MultiUse = -1 'True +END +Attribute VB_Name = "SQLSelect" +Attribute VB_GlobalNameSpace = False +Attribute VB_Creatable = False +Attribute VB_PredeclaredId = False +Attribute VB_Exposed = True +Option Explicit + +' Class: SQLSelect +' A SQL Select query +Implements iSQLQuery + +Private oSQL As SQLQuery +Private vFields() As Variant +Private bDistinct As Boolean +Private vGroupBy As Variant +Private oHaving As SQLCondition +Private oHavingGroup As SQLWhereGroup +Private aJoin() As Variant 'An array of arrays. Each array is a 'from_item' +Private aOrder() As Variant +Private sUnion() As Variant +'Limit +'Offset + +' Property: Table +Property Let Table(sValue As String) + addTable sValue, "" +End Property + +' Property: Fields +' The fields in the query +Property Let Fields(vValue) + Dim element As Variant + For Each element In vValue + AddField element + Next +End Property + +' Property: GroupBy +' The field to use for aggregration +Property Let GroupBy(vValue) + vGroupBy = vValue +End Property + +' Constructor: Class_Initialize +' Initializes class members +Private Sub Class_Initialize() + ReDim aJoin(0) + ReDim vFields(0) + ReDim aOrder(0) + vFields(0) = "" + aOrder(0) = "" + bDistinct = False + vGroupBy = Array() + Set oSQL = New SQLQuery +End Sub + +' Sub: addField +' Add a field to the query statement +Public Sub AddField(sField, Optional sAlias As String = "") + Dim ArrLen As Integer + ArrLen = UBound(vFields) + If ArrLen = 0 Then + If Not IsArray(vFields(0)) Then + ArrLen = -1 + End If + End If + ReDim Preserve vFields(0 To ArrLen + 1) + vFields(ArrLen + 1) = Array(sField, sAlias) +End Sub + +' Sub: addTable +' Add a table to the query statement +Public Sub addTable(sName As String, Optional sAlias As String = "") + aJoin = ArrayPush(aJoin, Array("", sName, sAlias, "")) +End Sub + +' Sub: AddHaving +' Add a having clause o the SQL statement +Public Sub AddHaving(Field, Value, Optional op As String = "=", Optional GroupType As String = "AND") + Dim NewHaving As New SQLCondition + If Not (oHaving Is Nothing) Then + NewHaving.Create Field, op, Value + Set oHavingGroup = New SQLWhereGroup + oHavingGroup.SetGroup oHaving, NewHaving, GroupType + + 'Clear SQLWhere since we are using SQLWhereGroup instead + Set oHaving = Nothing + ElseIf oHavingGroup Is Nothing Then + Set oHaving = New SQLCondition + oHaving.Create Field, op, Value + Else + NewHaving.Create Field, op, Value + oHavingGroup.AddWhere NewHaving, GroupType + End If + +End Sub + +' Sub: AddArgument +' Add an argument to the SQL Statement. +' An argument is a value for a placeholder. +Public Sub AddArgument(sName As String, vValue) + oSQL.AddArgument sName, vValue +End Sub + +' Sub: AddJoin +' Add a join condition to the SQL query +Public Sub AddJoin(sType As String, sTable As String, Optional sAlias As String = "", Optional sCondition As String = "") + 'Should Check that sType is either "INNER", "LEFT OUTER", or "RIGHT OUTER" + Dim JoinLen As Integer + JoinLen = UBound(aJoin) + ReDim Preserve aJoin(0 To JoinLen + 1) + aJoin(JoinLen + 1) = Array(sType, sTable, sAlias, sCondition) +End Sub + +' Sub: InnerJoin +' Add an inner join to the SQL query +Public Sub InnerJoin(sTable As String, sAlias As String, sCondition As String) + AddJoin "INNER", sTable, sAlias, sCondition +End Sub + +' Sub: LeftJoin +' Add a left join to the SQL query +Public Sub LeftJoin(sTable, sAlias, Optional sCondition As String = "") + AddJoin "LEFT OUTER", sTable, sAlias, sCondition +End Sub + +' Sub: RightJoin +' Add a right join to the SQL query +' Left Joins are prefered over Right. Please edit query to use a left join +Public Sub RightJoin(sTable, sAlias, Optional sCondition As String = "") + AddJoin "RIGHT OUTER", sTable, sAlias, sCondition +End Sub + +' Sub: Union +' Join two SQLSelect objects together with a union +' sType is either "", "ALL", or "DISTINCT" +Public Sub Union(oSelect As SQLSelect, Optional sType = "") + Dim UnionArray() As Variant + UnionArray = Array(SQLSelect, sType) + 'Add UnionArray to aUnion +End Sub + +' Sub: Distinct +' Set the distict flag to true or false +Public Sub Distinct(Optional bValue = True) + bDistinct = bValue +End Sub + +' Sub: AddWhere +' Add a where clause to the SQL query +Public Sub AddWhere(Field, Value, Optional op As String = "=", Optional GroupType As String = "AND") + oSQL.AddWhere Field, Value, op, GroupType +End Sub + +Public Sub AddWhereGroup(NewWhereGroup As SQLWhereGroup, Optional GroupType As String = "AND") + oSQL.AddWhereGroup NewWhereGroup, GroupType +End Sub + +Public Sub OrderBy(sField As String, Optional sDirection As String = "ASC") + If sDirection = "DESC" Then + sDirection = "DESC" + Else + sDirection = "ASC" + End If + Dim ArrLen As Integer + ArrLen = UBound(aOrder) + If ArrLen = 0 Then + If aOrder(0) = "" Then + ArrLen = -1 + End If + End If + ReDim Preserve aOrder(0 To ArrLen + 1) + aOrder(ArrLen + 1) = Array(sField, sDirection) +End Sub + +' Function: getByProperty +' Generate a query of the form +' SELECT sField FROM sTableValue WHERE sProperty = vValue +' +' Parameters: +' sTableValue - The table to select the data from +' sField - The field name +' sProperty - The field to filter by +' vValue - The value to filter by +Public Sub getByProperty(sTableValue As String, sField As String, sProperty As String, vValue) + addTable sTableValue, "" + AddField sField + AddWhere sProperty, vValue +End Sub + +' Function: iSQLQuery_ToString +' Create the query string +Public Function iSQLQuery_ToString() As String + Dim return_string As String + + If UBound(vFields) < 0 Then + return_string = "" + Else + return_string = "SELECT " & DistinctString & FieldList & " FROM " & _ + JoinString & oSQL.WhereString & GroupByString & HavingString & OrderByString + End If + iSQLQuery_ToString = oSQL.ReplaceArguments(return_string) +End Function + +Private Function DistinctString() As String + Dim sDistinct As String + sDistinct = "" + If bDistinct Then + sDistinct = "DISTINCT " + End If + DistinctString = sDistinct +End Function + +Private Function JoinString() + Dim R As Long + Dim Lines() As String + Dim Line As String + Dim LineArray As Variant + ReDim Lines(0 To UBound(aJoin)) + For R = 0 To UBound(aJoin) + LineArray = aJoin(R) + Line = "" + If LineArray(0) <> "" Then + Line = LineArray(0) & " JOIN " + ElseIf R > 0 Then + Lines(R - 1) = Lines(R - 1) & "," + End If + Line = Line & LineArray(1) + If LineArray(2) <> "" Then + Line = Line & " " & LineArray(2) + End If + If LineArray(3) <> "" Then + Line = Line & " ON " & LineArray(3) + End If + Lines(R) = Line + Next R + JoinString = Join(Lines, " ") +End Function + +Private Function GroupByString() As String + If UBound(vGroupBy) > -1 Then + GroupByString = " GROUP BY " & Join(vGroupBy, ", ") + End If +End Function + +Private Function HavingString() As String + Dim sHaving As String + If Not (oHaving Is Nothing And oHavingGroup Is Nothing) Then + If Not (oHaving Is Nothing) Then + sHaving = oHaving.toString + Else + sHaving = oHavingGroup.toString + End If + HavingString = " HAVING " & sHaving + End If +End Function + +Private Function OrderByString() As String + OrderByString = "" + If IsArray(aOrder(0)) Then + OrderByString = " ORDER BY " & JoinArrayofArrays(aOrder) + End If +End Function + +Private Function FieldList() + Dim R As Long + Dim Lines() As String + Dim Line As String + Dim LineArray As Variant + ReDim Lines(0 To UBound(vFields)) + For R = 0 To UBound(vFields) + LineArray = vFields(R) + Line = LineArray(0) + If LineArray(1) <> "" Then + Line = Line & " AS " & LineArray(1) + End If + Lines(R) = Line + Next R + FieldList = Join(Lines, ", ") +End Function + +Private Function UnionString() + Dim NewSelect As iSQLQuery + aUnion = UnionArray(0) + Set NewSelect = aUnion(0) + UnionString = " UNION " & NewSelect.toString() +End Function diff --git a/vba/vba7_1/examples/snips/test_class_declarations.cls b/vba/vba7_1/examples/snips/test_class_declarations.cls new file mode 100644 index 0000000000..d163429837 --- /dev/null +++ b/vba/vba7_1/examples/snips/test_class_declarations.cls @@ -0,0 +1,16 @@ +VERSION 1.0 CLASS +BEGIN + MultiUse = -1 'True +END +Attribute VB_GlobalNameSpace = True +Attribute VB_Creatable = True +Attribute VB_PredeclaredId = False +Attribute VB_Exposed = True +Attribute VB_Name = "ClassDeclarations" +Public Event Ev(ByVal Data As Variant) +Const Foo1 = 2 +Private Const Foo3 As [String] = "4" +Private Const Foo2 = 1 +Attribute Foo2.VB_VarHelpID = -1 +Public Function Foo() as Bar: End Function +Attribute Foo.VB_UserMemId = -4 diff --git a/vba/vba7_1/examples/snips/test_control_structures.bas b/vba/vba7_1/examples/snips/test_control_structures.bas new file mode 100644 index 0000000000..862a7db73e --- /dev/null +++ b/vba/vba7_1/examples/snips/test_control_structures.bas @@ -0,0 +1,4 @@ +Attribute VB_Name = "ControlStructures" +Function Foo(Index1) +If Index1 < 3 Then: Foo = 3 +End Function diff --git a/vba/vba7_1/examples/snips/test_function.bas b/vba/vba7_1/examples/snips/test_function.bas new file mode 100644 index 0000000000..30c399e830 --- /dev/null +++ b/vba/vba7_1/examples/snips/test_function.bas @@ -0,0 +1,17 @@ +Attribute VB_Name = "foo" +' Missing coverage for endLabel +Public Static Function Module() + +End Function + +Private Function Module As Boolean Static + callAnotherSub: + +' An identifierStatementLabel is recognized as a callStatement +' when in the endLabel position. +label1:End Function ' Comment + +Global Function Module#(Foo As Boolean, Optional Bar=5) +' A lineNumberLabel is recognised correctly in the +' endLabel position, but not as an endlabel. +2: End Function : REM sefkljnsa diff --git a/vba/vba7_1/examples/snips/test_function_names.bas b/vba/vba7_1/examples/snips/test_function_names.bas new file mode 100644 index 0000000000..6bbc26bd2d --- /dev/null +++ b/vba/vba7_1/examples/snips/test_function_names.bas @@ -0,0 +1,10 @@ +Attribute VB_Name = "TestFunctionNames" +' The word "String" is a reserved type, but is allowed +' as a function name +Function Baz() + Foo = String(5, 42) + Bar = Date + Foo2 = String$(5, 42) + Bar2 = Date$ + Foo3 = Abs(-1) +End Function diff --git a/vba/vba7_1/examples/snips/test_line_continuation.bas b/vba/vba7_1/examples/snips/test_line_continuation.bas new file mode 100644 index 0000000000..0b844e4c8e --- /dev/null +++ b/vba/vba7_1/examples/snips/test_line_continuation.bas @@ -0,0 +1,18 @@ +Attribute VB_Name = "LineContinuation" +Public _ +Sub _ +Module _ +( _ +) _ +: _ + Call _ + foo _ + ( _ + 1 _ + + _ + 3 _ + ): _ + Call foo _ + +End _ +Sub diff --git a/vba/vba7_1/examples/snips/test_literals.bas b/vba/vba7_1/examples/snips/test_literals.bas new file mode 100644 index 0000000000..c132786c95 --- /dev/null +++ b/vba/vba7_1/examples/snips/test_literals.bas @@ -0,0 +1,9 @@ +Attribute VB_Name = "TestLiterals" + +Function integers() + X = &H10 + X = &H10& + X = &04 + X = &O02 + X = &o02^ +End Function diff --git a/vba/vba7_1/examples/snips/test_statements.bas b/vba/vba7_1/examples/snips/test_statements.bas new file mode 100644 index 0000000000..c7eb61dc31 --- /dev/null +++ b/vba/vba7_1/examples/snips/test_statements.bas @@ -0,0 +1,61 @@ +Attribute VB_Name = "statements" + +Public Static Function Module() +Attribute Module.VB_Description = "My Description" +Attribute Module.VB_UserMemId = 0 + ' Call + Call bar + bar.Type baz:="7", foo:=5 + Call baz(4) + + 'While + While True + Wend + + ' For + For i = 0 To 7 Step 2 + Next + + For i = 0 To 7 Step 2 + Next i + + For i = 0 To 7 Step 2 + For j = 0 To 7 + sum = sum + i * j + Next j + for each j in k +next j, i + +select case foo + case bar: baz + case baz: quix +end select + + If True Then + ' Test out the end statement + End + ElseIf True Then 'skip + Call foo 'skip + Else + Call bar + EndIf + +'Multiple range-clause elements can be present in a case-clause. +Select Case Foo + case 1, 2, 3, 4 + bar = 3 + case 5, 6, 7 + baz = 4 + case is > 3 +End Select + + +' The Input function accepts a marked file number as a prameter +Open foo for Input as #ff + bar = Input(LOF(ff), #ff) +Close #ff + +' No WS around operators +Foo = (foo)/count +Debug.Print A B, C; D & E +End Function diff --git a/vba/vba7_1/examples/snips/test_sub.bas b/vba/vba7_1/examples/snips/test_sub.bas new file mode 100644 index 0000000000..4b8e97945b --- /dev/null +++ b/vba/vba7_1/examples/snips/test_sub.bas @@ -0,0 +1,12 @@ +Attribute VB_Name = "test_sub" +Public Static Sub Module + +End Sub + +Private Sub Module() Static + callAnotherSub: + +1: End Sub ' Comment + +Global Sub Module(Foo As Boolean, Optional Bar=2) +End Sub : REM sefkljnsa diff --git a/vba/vba7_1/examples/snips/test_variables.bas b/vba/vba7_1/examples/snips/test_variables.bas new file mode 100644 index 0000000000..83a4c428a2 --- /dev/null +++ b/vba/vba7_1/examples/snips/test_variables.bas @@ -0,0 +1,15 @@ +Attribute VB_Name = "TestVariables" +Function Foo() + Dim Bar() + Dim vbaTests(1 To 99, 1 To 2) As Variant + variable.[foreign name can be weird] + variable.[even @lik3 !" this.#] + variable.[also single quote's are fine] + + ' Redim Member access types + With foo.bar + ReDim .baz(0 To UBound(params(2))) + End With + Redim Preserve foo.bar(1 to foo.num) + ' MS-VBAL 3.2.1 states that final line may be non-terminated +End Function diff --git a/vba/vba7_1/pom.xml b/vba/vba7_1/pom.xml new file mode 100644 index 0000000000..a74006edd0 --- /dev/null +++ b/vba/vba7_1/pom.xml @@ -0,0 +1,57 @@ + + 4.0.0 + vba7_1 + jar + VBA 7.1 grammar + + org.antlr.grammars + vbaparent + 1.0-SNAPSHOT + + + + + org.antlr + antlr4-maven-plugin + ${antlr.version} + + ${basedir} + + vbaLexer.g4 + vbaParser.g4 + + true + true + + + + + antlr4 + + + + + + com.khubla.antlr + antlr4test-maven-plugin + ${antlr4test-maven-plugin.version} + + false + false + startRule + vba + + examples/ + + + + + test + + + + + + + diff --git a/vba/vba7_1/vbaLexer.g4 b/vba/vba7_1/vbaLexer.g4 new file mode 100644 index 0000000000..1bc3e0a765 --- /dev/null +++ b/vba/vba7_1/vbaLexer.g4 @@ -0,0 +1,1245 @@ +// $antlr-format alignTrailingComments true, columnLimit 150, minEmptyLines 1, maxEmptyLinesToKeep 1, reflowComments false, useTab false +// $antlr-format allowShortRulesOnASingleLine false, allowShortBlocksOnASingleLine true, alignSemicolons hanging, alignColons hanging + +lexer grammar vbaLexer; + +options { + caseInsensitive = true; +} + +// keywords +ABS + : 'ABS' + ; + +ACCESS + : 'ACCESS' + ; + +ADDRESSOF + : 'ADDRESSOF' + ; + +ALIAS + : 'ALIAS' + ; + +AND + : 'AND' + ; + +ANY + : 'ANY' + ; + +ATTRIBUTE + : 'ATTRIBUTE' + ; + +APPEND + : 'APPEND' + ; + +ARRAY + : 'ARRAY' + ; + +AS + : 'AS' + ; + +ASSERT + : 'ASSERT' + ; + +BASE + : 'BASE' + ; + +BEGIN + : 'BEGIN' + ; +BEGINPROPERTY + : 'BEGINPROPERTY' + ; + +BINARY + : 'BINARY' + ; + +BOOLEAN + : 'BOOLEAN' + ; + +BOOLEAN_B + : '[BOOLEAN]' + ; + +BYVAL + : 'BYVAL' + ; + +BYREF + : 'BYREF' + ; + +BYTE + : 'BYTE' + ; + +BYTE_B + : '[BYTE]' + ; + +CALL + : 'CALL' + ; + +CASE + : 'CASE' + ; + +CBOOL + : 'CBOOL' + ; + +CBYTE + : 'CBYTE' + ; + +CCUR + : 'CCUR' + ; + +CDATE + : 'CDATE' + ; + +CDBL + : 'CDBL' + ; + +CDEC + : 'CDEC' + ; + +CDECL + : 'CDECL' + ; + +CHDIR + : 'CHDIR' + ; + +CHDRIVE + : 'CHDRIVE' + ; + +CINT + : 'CINT' + ; + +CIRCLE + : 'CIRCLE' + ; + +CLASS + : 'CLASS' + ; + +CLASS_INITIALIZE + : 'CLASS_INITIALIZE' + ; + +CLASS_TERMINATE + : 'CLASS_TERMINATE' + ; + +CLNG + : 'CLNG' + ; + +CLNGLNG + : 'CLNGLNG' + ; + +CLNGPTR + : 'CLNGPTR' + ; + +CLOSE + : 'CLOSE' + ; + +COMPARE + : 'COMPARE' + ; + +CONST + : 'CONST' + ; + +CSNG + : 'CSNG' + ; + +CSTR + : 'CSTR' + ; + +CVAR + : 'CVAR' + ; + +CVERR + : 'CVERR' + ; + +CURRENCY + : 'CURRENCY' + ; + +CURRENCY_B + : '[CURRENCY]' + ; + +DATE + : 'DATE' + ; + +DATE_B + : '[DATE]' + ; + +DEBUG + : 'DEBUG' + ; + +DECLARE + : 'DECLARE' + ; + +DECIMAL + : 'DECIMAL' + ; + +DEFBOOL + : 'DEFBOOL' + ; + +DEFBYTE + : 'DEFBYTE' + ; + +DEFCUR + : 'DEFCUR' + ; + +DEFDATE + : 'DEFDATE' + ; + +DEFDBL + : 'DEFDBL' + ; + +DEFDEC + : 'DEFDEC' + ; + +DEFINT + : 'DEFINT' + ; + +DEFLNG + : 'DEFLNG' + ; + +DEFLNGLNG + : 'DEFLNGLNG' + ; + +DEFLNGPTR + : 'DEFLNGPTR' + ; + +DEFOBJ + : 'DEFOBJ' + ; + +DEFSNG + : 'DEFSNG' + ; + +DEFSTR + : 'DEFSTR' + ; + +DEFVAR + : 'DEFVAR' + ; + +DIM + : 'DIM' + ; + +DO + : 'DO' + ; + +DOEVENTS + : 'DOEVENTS' + ; + +DOUBLE + : 'DOUBLE' + ; + +DOUBLE_B + : '[DOUBLE]' + ; + +EACH + : 'EACH' + ; + +ELSE + : 'ELSE' + ; + +ELSEIF + : 'ELSEIF' + ; + +EMPTY_X + : 'EMPTY' + ; + +ENDIF + : 'ENDIF' + ; + +END + : 'END' + ; + +ENDPROPERTY + : 'ENDPROPERTY' + ; + +ENUM + : 'ENUM' + ; + +EQV + : 'EQV' + ; + +ERASE + : 'ERASE' + ; + +ERROR + : 'ERROR' + ; + +EVENT + : 'EVENT' + ; + +EXIT + : 'EXIT' + ; + +EXPLICIT + : 'EXPLICIT' + ; + +FALSE + : 'FALSE' + ; + +FIX + : 'FIX' + ; + +FRIEND + : 'FRIEND' + ; + +FOR + : 'FOR' + ; + +FUNCTION + : 'FUNCTION' + ; + +GET + : 'GET' + ; + +GLOBAL + : 'GLOBAL' + ; + +GO + : 'GO' + ; + +GOSUB + : 'GOSUB' + ; + +GOTO + : 'GOTO' + ; + +IF + : 'IF' + ; + +IMP + : 'IMP' + ; + +IMPLEMENTS + : 'IMPLEMENTS' + ; + +IN + : 'IN' + ; + +INPUT + : 'INPUT' + ; + +INPUTB + : 'INPUTB' + ; + +INT + : 'INT' + ; + +IS + : 'IS' + ; + +INTEGER + : 'INTEGER' + ; + +INTEGER_B + : '[INTEGER]' + ; + +KILL + : 'KILL' + ; + +LBOUND + : 'LBOUND' + ; + +LEN + : 'LEN' + ; + +LENB + : 'LENB' + ; + +LET + : 'LET' + ; + +LIB + : 'LIB' + ; + +LIKE + : 'LIKE' + ; + +LINE + : 'LINE' + ; + +LINEINPUT + : 'LINEINPUT' + ; + +LOCK + : 'LOCK' + ; + +LONG + : 'LONG' + ; + +LONG_B + : '[LONG]' + ; + +LONGLONG + : 'LONGLONG' + ; + +LONGLONG_B + : '[LONGLONG]' + ; + +LONGPTR + : 'LONGPTR' + ; + +LONGPTR_B + : '[LONGPTR]' + ; + +LOOP + : 'LOOP' + ; + +LSET + : 'LSET' + ; + +ME + : 'ME' + ; + +MID + : 'MID' + ; + +MIDB + : 'MIDB' + ; + +MID_D + : 'MID$' + ; + +MIDB_D + : 'MIDB$' + ; + +MOD + : 'MOD' + ; + +MODULE + : 'MODULE' + ; + +NEXT + : 'NEXT' + ; + +NEW + : 'NEW' + ; + +NOT + : 'NOT' + ; + +NOTHING + : 'NOTHING' + ; + +NULL_ + : 'NULL' + ; + +OBJECT + : 'OBJECT' + ; + +OBJECT_B + : '[OBJECT]' + ; + +ON + : 'ON' + ; + +OPEN + : 'OPEN' + ; + +OPTION + : 'OPTION' + ; + +OPTIONAL + : 'OPTIONAL' + ; + +OR + : 'OR' + ; + +OUTPUT + : 'OUTPUT' + ; + +PARAMARRAY + : 'PARAMARRAY' + ; + +PRESERVE + : 'PRESERVE' + ; + +PRINT + : 'PRINT' + ; + +PRIVATE + : 'PRIVATE' + ; + +PROPERTY + : 'PROPERTY' + ; + +PSET + : 'PSET' + ; + +PTRSAFE + : 'PTRSAFE' + ; + +PUBLIC + : 'PUBLIC' + ; + +PUT + : 'PUT' + ; + +RANDOM + : 'RANDOM' + ; + +RAISEEVENT + : 'RAISEEVENT' + ; + +READ + : 'READ' + ; + +REDIM + : 'REDIM' + ; + +REM + : 'REM' + ; + +RESET + : 'RESET' + ; + +RESUME + : 'RESUME' + ; + +RETURN + : 'RETURN' + ; + +RSET + : 'RSET' + ; + +SCALE + : 'SCALE' + ; + +SEEK + : 'SEEK' + ; + +SELECT + : 'SELECT' + ; + +SET + : 'SET' + ; + +SGN + : 'SGN' + ; + +SHARED + : 'SHARED' + ; + +SINGLE + : 'SINGLE' + ; + +SINGLE_B + : '[SINGLE]' + ; + +SPC + : 'SPC' + ; + +STATIC + : 'STATIC' + ; + +STEP + : 'STEP' + ; + +STOP + : 'STOP' + ; + +STRING + : 'STRING' + ; + +STRING_B + : '[STRING]' + ; + +SUB + : 'SUB' + ; + +TAB + : 'TAB' + ; + +TEXT + : 'TEXT' + ; + +THEN + : 'THEN' + ; + +TO + : 'TO' + ; + +TRUE + : 'TRUE' + ; + +TYPE + : 'TYPE' + ; + +TYPEOF + : 'TYPEOF' + ; + +UBOUND + : 'UBOUND' + ; + +UNLOCK + : 'UNLOCK' + ; + +UNTIL + : 'UNTIL' + ; + +VB_BASE + : 'VB_BASE' + ; + +VB_CONTROL + : 'VB_CONTROL' + ; + +VB_CREATABLE + : 'VB_CREATABLE' + ; + +VB_CUSTOMIZABLE + : 'VB_CUSTOMIZABLE' + ; + +VB_DESCRIPTION + : 'VB_DESCRIPTION' + ; + +VB_EXPOSED + : 'VB_EXPOSED' + ; + +VB_EXT_KEY + : 'VB_EXT_KEY ' + ; + +VB_GLOBALNAMESPACE + : 'VB_GLOBALNAMESPACE' + ; + +VB_HELPID + : 'VB_HELPID' + ; + +VB_INVOKE_FUNC + : 'VB_INVOKE_FUNC' + ; + +VB_INVOKE_PROPERTY + : 'VB_INVOKE_PROPERTY ' + ; + +VB_INVOKE_PROPERTYPUT + : 'VB_INVOKE_PROPERTYPUT' + ; + +VB_INVOKE_PROPERTYPUTREF + : 'VB_INVOKE_PROPERTYPUTREF' + ; + +VB_MEMBERFLAGS + : 'VB_MEMBERFLAGS' + ; + +VB_NAME + : 'VB_NAME' + ; + +VB_PREDECLAREDID + : 'VB_PREDECLAREDID' + ; + +VB_PROCDATA + : 'VB_PROCDATA' + ; + +VB_TEMPLATEDERIVED + : 'VB_TEMPLATEDERIVED' + ; + +VB_USERMEMID + : 'VB_USERMEMID' + ; + +VB_VARDESCRIPTION + : 'VB_VARDESCRIPTION' + ; + +VB_VARHELPID + : 'VB_VARHELPID' + ; + +VB_VARMEMBERFLAGS + : 'VB_VARMEMBERFLAGS' + ; + +VB_VARPROCDATA + : 'VB_VARPROCDATA ' + ; + +VB_VARUSERMEMID + : 'VB_VARUSERMEMID' + ; + +VARIANT + : 'VARIANT' + ; + +VARIANT_B + : '[VARIANT]' + ; + +VERSION + : 'VERSION' + ; + +WEND + : 'WEND' + ; + +WHILE + : 'WHILE' + ; + +WIDTH + : 'WIDTH' + ; + +WITH + : 'WITH' + ; + +WITHEVENTS + : 'WITHEVENTS' + ; + +WRITE + : 'WRITE' + ; + +XOR + : 'XOR' + ; + +// Standard Library functions, subs, and properties +// should these be removed? +APPACTIVATE + : 'APPACTIVATE' + ; + +COLLECTION + : 'COLLECTION' + ; + +DATABASE + : 'DATABASE' + ; + +DELETESETTING + : 'DELETESETTING' + ; + +FILECOPY + : 'FILECOPY' + ; + +MKDIR + : 'MKDIR' + ; + +NAME + : 'NAME' + ; + +RANDOMIZE + : 'RANDOMIZE' + ; + +RMDIR + : 'RMDIR' + ; + +SAVEPICTURE + : 'SAVEPICTURE' + ; + +SAVESETTING + : 'SAVESETTING' + ; + +SENDKEYS + : 'SENDKEYS' + ; + +SETATTR + : 'SETATTR' + ; + +TIME + : 'TIME' + ; + +LOAD + : 'LOAD' + ; + +UNLOAD + : 'UNLOAD' + ; + +// symbols +AMPERSAND + : '&' + ; + +ASPERAND + : '@' + ; + +ASSIGN + : ':=' + ; + +COMMA + : ',' + ; + +DIV + : '\\' + | '/' + ; +Dollar + : '$' + ; +EQ + : '=' + ; +EXCLAM + : '!' + ; +GEQ + : '>=' + | '=>' + ; + +GT + : '>' + ; + +HASH + : '#' + ; +LEQ + : '<=' + | '=<' + ; + +LPAREN + : '(' + ; + +LT + : '<' + ; + +MINUS + : '-' + ; + +MINUS_EQ + : '-=' + ; + +MULT + : '*' + ; + +NEQ + : '<>' + | '><' + ; + +PERCENT + : '%' + ; + +PERIOD + : '.' + ; + +PLUS + : '+' + ; + +PLUS_EQ + : '+=' + ; + +POW + : '^' + ; + +RPAREN + : ')' + ; + +SEMICOLON + : ';' + ; + +L_SQUARE_BRACKET + : '[' + ; + +R_SQUARE_BRACKET + : ']' + ; + +// literals +fragment BLOCK + : HEXDIGIT HEXDIGIT HEXDIGIT HEXDIGIT + ; + +GUID + : '{' BLOCK BLOCK MINUS BLOCK MINUS BLOCK MINUS BLOCK MINUS BLOCK BLOCK BLOCK '}' + ; + +STRINGLITERAL + : '"' (~["\r\n] | '""')* '"' + ; + +INTEGERLITERAL + : (DIGIT DIGIT* + | '&H' [0-9A-F]+ + | '&' [O]? [0-7]+) [%&^]? + ; + +FLOATLITERAL + : FLOATINGPOINTLITERAL [!#@]? + | DECIMALLITERAL [!#@] + ; + +fragment FLOATINGPOINTLITERAL + : DECIMALLITERAL [DE] [+-]? DECIMALLITERAL + | DECIMALLITERAL '.' DECIMALLITERAL? ([DE] [+-]? DECIMALLITERAL)? + | '.' DECIMALLITERAL ([DE] [+-]? DECIMALLITERAL)? + ; + +fragment DECIMALLITERAL + : DIGIT DIGIT* + ; + +DATELITERAL + : '#' DATEORTIME '#' + ; + +fragment DATEORTIME + : DATEVALUE WS+ TIMEVALUE + | DATEVALUE + | TIMEVALUE + ; + +fragment DATEVALUE + : DATEVALUEPART DATESEPARATOR DATEVALUEPART (DATESEPARATOR DATEVALUEPART)? + ; + +fragment DATEVALUEPART + : DIGIT+ + | MONTHNAME + ; + +fragment DATESEPARATOR + : WS+ + | WS? [/,-] WS? + ; + +fragment MONTHNAME + : ENGLISHMONTHNAME + | ENGLISHMONTHABBREVIATION + ; + +fragment ENGLISHMONTHNAME + : 'JANUARY' + | 'FEBRUARY' + | 'MARCH' + | 'APRIL' + | 'MAY' + | 'JUNE' + | 'JULY' + | 'AUGUST' + | 'SEPTEMBER' + | 'OCTOBER' + | 'NOVEMBER' + | 'DECEMBER' + ; + +// May has intentionally been left out +fragment ENGLISHMONTHABBREVIATION + : 'JAN' + | 'FEB' + | 'MAR' + | 'APR' + | 'JUN' + | 'JUL' + | 'AUG' + | 'SEP' + | 'OCT' + | 'NOV' + | 'DEC' + ; + +fragment TIMEVALUE + : DIGIT+ AMPM + | DIGIT+ TIMESEPARATOR DIGIT+ (TIMESEPARATOR DIGIT+)? AMPM? + ; + +fragment TIMESEPARATOR + : WS? (':' | '.') WS? + ; + +fragment AMPM + : WS? ('AM' | 'PM' | 'A' | 'P') + ; + +FILEOFFSET + : '$'? STRINGLITERAL ':' HEXDIGIT+ + ; +// whitespace, line breaks, comments, ... +LINE_CONTINUATION + : WS UNDERSCORE WS? '\r'? '\n' + ; + +NEWLINE + : [\r\n\u2028\u2029]+ + ; + +REMCOMMENT + : COLON? REM WS (LINE_CONTINUATION | ~[\r\n\u2028\u2029])* + ; + +COMMENT + : SINGLEQUOTE (LINE_CONTINUATION | ~[\r\n\u2028\u2029])* + ; + +SINGLEQUOTE + : '\'' + ; + +COLON + : ':' + ; + +UNDERSCORE + : '_' + ; + +WS + : ([ \t\u0019\u3000])+ + ; + +MACRO_LINE + : (WS? '#IF' ~[\r\n\u2028\u2029]* THEN COMMENT? + | WS? '#ELSEIF' ~[\r\n\u2028\u2029]* THEN COMMENT? + | WS? '#ELSE' COMMENT? + | WS? ('#END If'|'#endif') COMMENT?) -> channel(HIDDEN) + ; + +// identifier +IDENTIFIER + : [A-Z][A-Z0-9_]* + ; + +FOREIGN_NAME + : '[' ~[\r\n\u2028\u2029]* ']' + ; + +// letters +fragment LETTER + : [A-Z_\p{L}] + ; + +fragment DIGIT + : [0-9] + ; + +fragment HEXDIGIT + : [A-F0-9] + ; + +fragment LETTERORDIGIT + : [A-Z0-9_\p{L}] + ; diff --git a/vba/vba7_1/vbaParser.g4 b/vba/vba7_1/vbaParser.g4 new file mode 100644 index 0000000000..cf6e17f25a --- /dev/null +++ b/vba/vba7_1/vbaParser.g4 @@ -0,0 +1,1466 @@ +/* +* Visual Basic 7.1 Grammar for ANTLR4 +* +* Derived from the Visual Basic 7.1 language reference +* https://officeprotocoldoc.z19.web.core.windows.net/files/MS-VBAL/%5bMS-VBAL%5d.pdf +* Last compared to Protocol Revision 2.4 +*/ + +// $antlr-format alignTrailingComments true, columnLimit 150, minEmptyLines 1, maxEmptyLinesToKeep 1, reflowComments false, useTab false +// $antlr-format allowShortRulesOnASingleLine false, allowShortBlocksOnASingleLine true, alignSemicolons hanging, alignColons hanging + +parser grammar vbaParser; + + +options { + caseInsensitive = true; + tokenVocab = vbaLexer; +} + +// Contexts not listed in the specification +// Everything until section 5.1 is typically machine generated code. +startRule + : module EOF + ; + +// Added form file entry +module + : endOfLineNoWs* ( + proceduralModule + | classFileHeader classModule + | formFileHeader classModule + ) endOfLine* WS? + ; + +classFileHeader + : classVersionIdentification classBeginBlock + ; + +classVersionIdentification + : VERSION WS FLOATLITERAL WS CLASS + ; + +classBeginBlock + : endOfLine+ BEGIN beginBlockConfigElement+ endOfLine+ END + ; + +beginBlockConfigElement + : endOfLine+ (OBJECT '.')? '_'? ambiguousIdentifier WS? EQ WS? (('-'? literalExpression) | FILEOFFSET) + | formBeginBlock + | beginPropertyBlock + ; + +// Form entries +formFileHeader + : formVersionIdentification (formObjectAssign)* formBeginBlock + ; + +formVersionIdentification + : VERSION WS FLOATLITERAL + ; +formObjectAssign + : endOfLine+ OBJECT WS? EQ WS? STRINGLITERAL (';' WS? STRINGLITERAL)? + ; +formBeginBlock + : endOfLine+ BEGIN WS (GUID | (ambiguousIdentifier '.' ambiguousIdentifier)) WS ambiguousIdentifier beginBlockConfigElement+ endOfLine+ END + ; +beginPropertyBlock + : endOfLine+ BEGINPROPERTY WS ambiguousIdentifier (WS GUID WS?)? beginBlockConfigElement+ endOfLine+ ENDPROPERTY + ; + +//--------------------------------------------------------------------------------------- +// 4.2 Modules +proceduralModule + : proceduralModuleHeader endOfLineNoWs* proceduralModuleBody + ; +classModule + : classModuleHeader endOfLine* classModuleBody + ; + +// Compare STRINGLITERAL to quoted-identifier +proceduralModuleHeader + : endOfLine* ATTRIBUTE WS? VB_NAME WS? EQ WS? STRINGLITERAL + ; +classModuleHeader: (endOfLine+ classAttr)+ WS?; + +// VBA Library Projects are allowed to have GoobalNamespace and creatable as true. +classAttr + : ATTRIBUTE WS? VB_NAME WS? EQ WS? STRINGLITERAL + | ATTRIBUTE WS? VB_GLOBALNAMESPACE WS? EQ WS? booleanLiteralIdentifier + | ATTRIBUTE WS? VB_CREATABLE WS? EQ WS? booleanLiteralIdentifier + | ATTRIBUTE WS? VB_PREDECLAREDID WS? EQ WS? booleanLiteralIdentifier + | ATTRIBUTE WS? VB_EXPOSED WS? EQ WS? booleanLiteralIdentifier + | ATTRIBUTE WS? VB_CUSTOMIZABLE WS? EQ WS? booleanLiteralIdentifier + ; +//--------------------------------------------------------------------------------------- +// 5.1 Module Body Structure +// Everything from here down is user generated code. +proceduralModuleBody: proceduralModuleDeclarationSection? endOfLine* proceduralModuleCode; +classModuleBody: classModuleDeclarationSection? classModuleCode; +unrestrictedName + : reservedIdentifier + | name + ; + +// Added markedFileNumber to fix a bug +name + : untypedName + | typedName + | markedFileNumber + ; +untypedName + : ambiguousIdentifier + | FOREIGN_NAME + ; + +//--------------------------------------------------------------------------------------- +// 5.2 Module Declaration Section Structure +proceduralModuleDeclarationSection + : (endOfLine+ proceduralModuleDeclarationElement)+ + | ((endOfLine+ proceduralModuleDirectiveElement)* endOfLine+ defDirective) (proceduralModuleDeclarationElement endOfLineNoWs)* + ; +classModuleDeclarationSection + : (classModuleDeclarationElement endOfLine+)+ + | ((classModuleDirectiveElement endOfLine+)* defDirective) (classModuleDeclarationElement endOfLine+)* + ; +proceduralModuleDirectiveElement + : commonOptionDirective + | optionPrivateDirective + | defDirective + ; +proceduralModuleDeclarationElement + : commonModuleDeclarationElement + | globalVariableDeclaration + | publicConstDeclaration + | publicExternalProcedureDeclaration + | globalEnumDeclaration + | commonOptionDirective + | optionPrivateDirective + ; +classModuleDirectiveElement + : commonOptionDirective + | defDirective + | implementsDirective + ; +classModuleDeclarationElement + : commonModuleDeclarationElement + | eventDeclaration + | commonOptionDirective + | implementsDirective + ; + +// 5.2.1 Option Directives +commonOptionDirective + : optionCompareDirective + | optionBaseDirective + | optionExplicitDirective + | remStatement + ; + +// 5.2.1.1 Option Compare Directive +optionCompareDirective: OPTION wsc COMPARE wsc (BINARY | TEXT); + +// 5.2.1.2 Option Base Directive +// INTEGER or SHORT? +optionBaseDirective: OPTION wsc BASE wsc INTEGERLITERAL; + +// 5.2.1.3 Option Explicit Directive +optionExplicitDirective: OPTION wsc EXPLICIT; + +// 5.2.1.4 Option Private Directive +optionPrivateDirective: OPTION wsc PRIVATE wsc MODULE; + +// 5.2.2 Implicit Definition Directives +defDirective: defType WS letterSpec (WS ',' WS letterSpec)*; +letterSpec + : singleLetter + | universalLetterRange + | letterRange + ; +singleLetter: ambiguousIdentifier; +universalLetterRange: upperCaseA WS '-' WS upperCaseZ; +upperCaseA: ambiguousIdentifier; +upperCaseZ: ambiguousIdentifier; +letterRange: firstLetter WS '-' WS lastLetter; +firstLetter: ambiguousIdentifier; +lastLetter: ambiguousIdentifier; +defType + : DEFBOOL + | DEFBYTE + | DEFCUR + | DEFDATE + | DEFDBL + | DEFINT + | DEFINT + | DEFLNG + | DEFLNGLNG + | DEFLNGPTR + | DEFOBJ + | DEFSNG + | DEFSTR + | DEFVAR + ; + +// 5.2.3 Module Declarations +// added public-type to fix bug +commonModuleDeclarationElement + : moduleVariableDeclaration + | privateConstDeclaration + | privateTypeDeclaration + | publicTypeDeclaration + | privateEnumDeclaration + | publicEnumDeclaration + | privateExternalProcedureDeclaration + ; + +// 5.2.3.1 Module Variable Declaration Lists +// Added variableHelpAttribute, not in MS-VBAL +moduleVariableDeclaration + : publicVariableDecalation + | privateVariableDeclaration + | variableHelpAttribute + ; + +variableHelpAttribute + : ATTRIBUTE WS ambiguousIdentifier '.' VB_VARHELPID WS? '=' WS? '-'? INTEGERLITERAL + ; +globalVariableDeclaration: GLOBAL WS variableDeclarationList; +publicVariableDecalation: PUBLIC (WS SHARED)? WS moduleVariableDeclarationList; +privateVariableDeclaration: ((PRIVATE | DIM) wsc) (SHARED wsc)? moduleVariableDeclarationList; +moduleVariableDeclarationList: (witheventsVariableDcl | variableDcl) (wsc? ',' wsc? (witheventsVariableDcl | variableDcl))*; +variableDeclarationList: variableDcl (wsc? ',' wsc? variableDcl)*; + +// 5.2.3.1.1 Variable Declarations +variableDcl + : typedVariableDcl + | untypedVariableDcl + ; +typedVariableDcl: typedName wsc? arrayDim?; +untypedVariableDcl: ambiguousIdentifier wsc? (arrayClause | asClause)?; +arrayClause: arrayDim (wsc asClause)?; +asClause + : asAutoObject + | asType + ; + +// 5.2.3.1.2 WithEvents Variable Declarations +witheventsVariableDcl: WITHEVENTS wsc ambiguousIdentifier wsc AS wsc? classTypeName; +classTypeName: definedTypeExpression; + +// 5.2.3.1.3 Array Dimensions and Bounds +arrayDim: '(' wsc? boundsList? wsc? ')'; +boundsList: dimSpec (wsc? ',' wsc? dimSpec)*; +dimSpec: lowerBound? wsc? upperBound; +lowerBound: constantExpression wsc TO wsc; +upperBound: constantExpression; + +// 5.2.3.1.4 Variable Type Declarations +asAutoObject: AS WS NEW WS classTypeName; +asType: AS WS typeSpec; +typeSpec + : fixedLengthStringSpec + | typeExpression + ; +fixedLengthStringSpec: STRING WS '*' WS stringLength; +stringLength + : INTEGERLITERAL + | constantName + ; +constantName: simpleNameExpression; + +// 5.2.3.2 Const Declarations +publicConstDeclaration: (GLOBAL | PUBLIC) wsc moduleConstDeclaration; +privateConstDeclaration: (PRIVATE wsc)? moduleConstDeclaration; +moduleConstDeclaration: constDeclaration; +constDeclaration: CONST wsc constItemList; +constItemList: constItem (wsc? ',' wsc? constItem)*; +constItem + : typedNameConstItem + | untypedNameConstItem + ; +typedNameConstItem: typedName wsc? EQ wsc? constantExpression; +untypedNameConstItem: ambiguousIdentifier (wsc constAsClause)? wsc? EQ wsc? constantExpression; +constAsClause: AS wsc builtinType; + +// 5.2.3.3 User Defined Type Declarations +publicTypeDeclaration: ((GLOBAL | PUBLIC) wsc)? udtDeclaration; +privateTypeDeclaration: PRIVATE wsc udtDeclaration; +udtDeclaration: TYPE wsc untypedName endOfStatement+ udtMemberList endOfStatement+ END wsc TYPE; +udtMemberList: udtElement (endOfStatement udtElement)*; +udtElement + : remStatement + | udtMember + ; +udtMember + : reservedNameMemberDcl + | untypedNameMemberDcl + ; +untypedNameMemberDcl: ambiguousIdentifier optionalArrayClause; +reservedNameMemberDcl: reservedMemberName wsc asClause; +optionalArrayClause: arrayDim? wsc asClause; +reservedMemberName + : statementKeyword + | markerKeyword + | operatorIdentifier + | specialForm + | reservedName + | literalIdentifier + | reservedForImplementationUse + | futureReserved + ; + +// 5.2.3.4 Enum Declarations +globalEnumDeclaration: GLOBAL wsc enumDeclaration; +publicEnumDeclaration: (PUBLIC wsc)? enumDeclaration; +privateEnumDeclaration: PRIVATE wsc enumDeclaration; +enumDeclaration: ENUM wsc untypedName endOfStatement+ enumMemberList endOfStatement+ END wsc ENUM ; +enumMemberList: enumElement (endOfStatement enumElement)*; +enumElement + : remStatement + | enumMember + ; +enumMember: untypedName (wsc? EQ wsc? constantExpression)?; + +// 5.2.3.5 External Procedure Declaration +publicExternalProcedureDeclaration: (PUBLIC wsc)? externalProcDcl; +privateExternalProcedureDeclaration: PRIVATE wsc externalProcDcl; +externalProcDcl: DECLARE wsc (PTRSAFE wsc)? (externalSub | externalFunction); +externalSub: SUB wsc subroutineName wsc libInfo (wsc procedureParameters)?; +externalFunction: FUNCTION wsc functionName wsc libInfo (wsc procedureParameters)? (wsc functionType)?; +libInfo: libClause (wsc aliasClause)?; +libClause: LIB wsc STRINGLITERAL; +aliasClause: ALIAS wsc STRINGLITERAL; + +// 5.2.4 Class Module Declarations +// 5.2.4.2 Implements Directive +implementsDirective: IMPLEMENTS WS classTypeName; + +// 5.2.4.3 Event Declaration +eventDeclaration: PUBLIC? wsc EVENT wsc ambiguousIdentifier eventParameterList?; +eventParameterList: '(' wsc? positionalParameters? wsc? ')'; + + +//--------------------------------------------------------------------------------------- +// 5.3 Module Code Section Structure +// removed an EOS +proceduralModuleCode: (proceduralModuleCodeElement endOfLine*)*; +classModuleCode: (classModuleCodeElement endOfLine*)*; +proceduralModuleCodeElement: commonModuleCodeElement; +classModuleCodeElement + : commonModuleCodeElement + | implementsDirective + ; + +// Added AttributeStatement. +commonModuleCodeElement + : remStatement + | procedureDeclaration + | attributeStatement + ; +procedureDeclaration + : subroutineDeclaration + | functionDeclaration + | propertyGetDeclaration + | propertyLhsDeclaration + ; + +// 5.3.1 Procedure Declarations +// Allow a static keyword before or after, but not both +subroutineDeclaration + : (procedureScope wsc)? ( + ((initialStatic wsc)? SUB wsc subroutineName (wsc? procedureParameters?)) + | (SUB wsc subroutineName (wsc? procedureParameters)? wsc? trailingStatic) + ) + procedureBody? + endLabel? endOfStatement+ END wsc SUB procedureTail?; +functionDeclaration + : (procedureScope wsc)? ( + (initialStatic wsc)? FUNCTION wsc functionName (wsc? procedureParameters)? (wsc? functionType)? + | FUNCTION wsc functionName (wsc? procedureParameters)? (wsc? functionType)? wsc? trailingStatic) + procedureBody? + endLabel? endOfStatement+ END wsc FUNCTION procedureTail?; + +propertyGetDeclaration + : (procedureScope wsc)? ( + (initialStatic wsc)? PROPERTY wsc GET wsc functionName (wsc? procedureParameters)? (wsc? functionType)? + | PROPERTY wsc GET wsc functionName procedureParameters? (wsc? functionType)? wsc? trailingStatic) + procedureBody? + endLabel? endOfStatement+ END wsc PROPERTY procedureTail?; + +propertyLhsDeclaration + : (procedureScope wsc)? ( + (initialStatic wsc)? PROPERTY wsc (LET | SET) wsc subroutineName wsc? propertyParameters + | PROPERTY wsc (LET | SET) wsc subroutineName propertyParameters wsc? trailingStatic) + procedureBody? + endLabel? endOfStatement+ END wsc PROPERTY procedureTail?; +endLabel: endOfStatement* endOfLineNoWs statementLabelDefinition; +procedureTail + : wsc? NEWLINE + | wsc? commentBody + | WS? ':' WS? remStatement + ; + +// 5.3.1.1 Procedure Scope +procedureScope + : PRIVATE + | PUBLIC + | FRIEND + | GLOBAL + ; + +// 5.3.1.2 Static Procedures +initialStatic: STATIC; +trailingStatic: STATIC; + +// 5.3.1.3 Procedure Names +subroutineName + : ambiguousIdentifier + | prefixedName + ; +functionName + : typedName + | ambiguousIdentifier + | prefixedName + ; +prefixedName + : eventHandlerName + | implementedName + | lifecycleHandlerName + ; + +// 5.3.1.4 Function Type Declarations +functionType: AS wsc typeExpression wsc? arrayDesignator?; +arrayDesignator: '(' wsc? ')'; + +// 5.3.1.5 Parameter Lists +procedureParameters: '(' wsc? parameterList? wsc? ')'; +propertyParameters: '(' wsc? (parameterList wsc? ',' wsc?)? valueParam wsc? ')'; +parameterList + : (positionalParameters wsc? ',' wsc? optionalParameters) + | (positionalParameters (wsc? ',' wsc? paramArray)?) + | optionalParameters + | paramArray + ; + +positionalParameters: positionalParam (wsc? ',' wsc? positionalParam)*; +optionalParameters: optionalParam (wsc? ',' wsc? optionalParam)*; +valueParam: positionalParam; +positionalParam: (parameterMechanism wsc)? paramDcl; +optionalParam + : optionalPrefix wsc paramDcl wsc? defaultValue?; +paramArray + : PARAMARRAY wsc ambiguousIdentifier '(' wsc? ')' (wsc AS wsc (VARIANT | '[' VARIANT ']'))?; +paramDcl + : untypedNameParamDcl + | typedNameParamDcl + ; +untypedNameParamDcl: ambiguousIdentifier parameterType?; +typedNameParamDcl: typedName arrayDesignator?; +optionalPrefix + : OPTIONAL (wsc parameterMechanism)? + | parameterMechanism wsc OPTIONAL + ; +parameterMechanism + : BYVAL + | BYREF + ; +parameterType: arrayDesignator? wsc AS wsc (typeExpression | ANY); +defaultValue: '=' wsc? constantExpression; + +// 5.3.1.8 Event Handler Declarations +eventHandlerName: ambiguousIdentifier; + +// 5.3.1.9 Implemented Name Declarations +implementedName: ambiguousIdentifier; + +// 5.3.1.10 Lifecycle Handler Declarations +lifecycleHandlerName + : CLASS_INITIALIZE + | CLASS_TERMINATE + ; + +//--------------------------------------------------------------------------------------- +// 5.4 Procedure Bodies and Statements +procedureBody: statementBlock; + +// 5.4.1 Statement Blocks +// spec used *, changed to + changed all parent to call with ? to avoid empty context. +// Made EOS optional to be able to force EOL before ifStatement elements. +statementBlock + : blockStatement+ + ; +blockStatement + : endOfStatement* endOfLineNoWs statementLabelDefinition + | endOfStatement+ remStatement + | statement + | endOfStatement* endOfLineNoWs attributeStatement + ; +statement + : endOfStatement+ fileStatement + | controlStatement + | endOfStatement+ dataManipulationStatement + | endOfStatement+ errorHandlingStatement + ; + +// 5.4.1.1 Statement Labels +statementLabelDefinition + : identifierStatementLabel ':' + | lineNumberLabel ':'? + ; +statementLabel + : identifierStatementLabel + | lineNumberLabel + ; +statementLabelList: statementLabel (wsc? ',' wsc? statementLabel)?; +identifierStatementLabel: ambiguousIdentifier; +lineNumberLabel: INTEGERLITERAL; + +// 5.4.1.2 Rem Statement +// We have a token for this +remStatement: REMCOMMENT; + +// 5.4.2 Control Statements +controlStatement + : endOfStatement* endOfLine+ ifStatement + | endOfStatement+ controlStatementExceptMultilineIf + ; +controlStatementExceptMultilineIf + : assertStatement + | callStatement + | whileStatement + | forStatement + | exitForStatement + | doStatement + | exitDoStatement + | singleLineIfStatement + | selectCaseStatement + | stopStatement + | gotoStatement + | onGotoStatement + | gosubStatement + | returnStatement + | onGosubStatement + | forEachStatement + | exitSubStatement + | exitFunctionStatement + | exitPropertyStatement + | raiseeventStatement + | withStatement + | endStatement + ; + +// 5.4.2.1 Call Statement +callStatement + : CALL wsc (simpleNameExpression + | memberAccessExpression + | indexExpression + | withExpression) + | (simpleNameExpression + | memberAccessExpression + | withExpression) (wsc argumentList)? + ; + +// 5.4.2.2 While Statement +whileStatement + : WHILE wsc booleanExpression + statementBlock? endOfStatement+ WEND; + +// 5.4.2.3 For Statement +forStatement + : simpleForStatement + | explicitForStatement + ; +simpleForStatement: forClause statementBlock? endOfStatement+ NEXT; +explicitForStatement + : forClause statementBlock? endOfStatement+ (NEXT | (nestedForStatement wsc? ',')) wsc boundVariableExpression; +nestedForStatement + : explicitForStatement + | explicitForEachStatement + ; +forClause + : FOR wsc boundVariableExpression wsc? EQ wsc? startValue wsc TO wsc endValue (wsc stepClause)?; +startValue: expression; +endValue: expression; +stepClause: STEP wsc stepIncrement; +stepIncrement: expression; + +// 5.4.2.4 For Each Statement +forEachStatement + : simpleForEachStatement + | explicitForEachStatement + ; +simpleForEachStatement + : forEachClause statementBlock? endOfStatement+ NEXT; + +explicitForEachStatement + : forEachClause statementBlock? + endOfStatement (NEXT | (nestedForStatement wsc? ',')) wsc boundVariableExpression; + forEachClause: FOR wsc EACH wsc? boundVariableExpression wsc? IN wsc? collection; + collection: expression; + +// 5.4.2.5 Exit For Statement +exitForStatement: EXIT wsc FOR; + +// 5.4.2.6 Do Statement +doStatement + : DO (wsc? conditionClause)? statementBlock? endOfStatement+ + LOOP (wsc? conditionClause)?; +conditionClause + : whileClause + | untilClause + ; +whileClause: WHILE wsc? booleanExpression; +untilClause: UNTIL wsc? booleanExpression; + +// 5.4.2.7 Exit Do Statement +exitDoStatement: EXIT wsc DO; + +// 5.4.2.8 If Statement +// why is a LINE-START required before this? +ifStatement + : IF wsc? booleanExpression wsc? THEN + statementBlock? + elseIfBlock* + elseBlock? endOfStatement+ + ((END wsc IF) | ENDIF); +// Need to verify why some of the end-of-line / line-start things are set the way they are. +elseIfBlock + : endOfStatement* endOfLine ELSEIF wsc? booleanExpression wsc? THEN endOfLine? + statementBlock? + | endOfStatement* ELSEIF wsc? booleanExpression wsc? THEN statementBlock? + ; +elseBlock: endOfLine+ ELSE endOfLine? wsc? statementBlock?; + +// 5.4.2.9 Single-line If Statement +singleLineIfStatement + : ifWithNonEmptyThen + | ifWithEmptyThen + ; +ifWithNonEmptyThen + : IF wsc booleanExpression wsc THEN wsc? listOrLabel (wsc singleLineElseClause)?; +ifWithEmptyThen + : IF wsc booleanExpression wsc THEN wsc singleLineElseClause; +singleLineElseClause: ELSE wsc? listOrLabel?; +listOrLabel + : (statementLabel (':' wsc? sameLineStatement?)*) + | ':'? wsc? sameLineStatement (wsc? ':' wsc? sameLineStatement?)* + ; +sameLineStatement + : fileStatement + | errorHandlingStatement + | dataManipulationStatement + | controlStatementExceptMultilineIf + ; + +// 5.4.2.10 Select Case Statement +selectCaseStatement + : SELECT wsc CASE wsc selectExpression + caseClause* + caseElseClause? + endOfStatement+ END wsc SELECT; +caseClause: endOfStatement+ CASE wsc? rangeClause (wsc? ',' wsc? rangeClause)* statementBlock?; +caseElseClause: endOfStatement+ CASE wsc ELSE statementBlock?; +rangeClause + : expression + | startValue wsc? TO wsc? endValue + | IS? wsc comparisonOperator wsc? expression; +selectExpression: expression; +comparisonOperator + : EQ + | NEW + | LT + | GT + | LEQ + | GEQ + ; + +// 5.4.2.11 Stop Statement +stopStatement: STOP; + +// 5.4.2.12 GoTo Statement +gotoStatement: (GO wsc TO | GOTO) wsc statementLabel; + +// 5.4.2.13 On…GoTo Statement +onGotoStatement: ON wsc? expression GOTO wsc statementLabelList; + +// 5.4.2.14 GoSub Statement +gosubStatement: ((GO wsc SUB) | GOSUB) wsc statementLabel; + +// 5.4.2.15 Return Statement +returnStatement: RETURN; + +// 5.4.2.16 On…GoSub Statement +onGosubStatement: ON wsc? expression wsc? GOSUB wsc statementLabelList; + +// 5.4.2.17 Exit Sub Statement +exitSubStatement: EXIT wsc SUB; + +// 5.4.2.18 Exit Function Statement +exitFunctionStatement: EXIT wsc FUNCTION; + +// 5.4.2.19 Exit Property Statement +exitPropertyStatement: EXIT wsc PROPERTY; + +// 5.4.2.20 RaiseEvent Statement +raiseeventStatement + : RAISEEVENT wsc? ambiguousIdentifier wsc? ('(' wsc? eventArgumentList wsc? ')')?; +eventArgumentList: (eventArgument (wsc? ',' wsc? eventArgument)*)?; +eventArgument: expression; + +// 5.4.2.21 With Statement +withStatement: WITH wsc? expression statementBlock? endOfStatement+ END wsc WITH; + +// 5.4.2.22 End Statement +endStatement + : END + ; + +// 5.4.2.22 Assert Statement +assertStatement + : DEBUG '.' ASSERT wsc booleanExpression + ; + +// 5.4.3 Data Manipulation Statements +// Added eraseStatement. It is missing from the list in MsS-VBAL 1.7 +dataManipulationStatement + : localVariableDeclaration + | staticVariableDeclaration + | localConstDeclaration + | redimStatement + | eraseStatement + | midStatement + | rsetStatement + | lsetStatement + | letStatement + | setStatement + ; + +// 5.4.3.1 Local Variable Declarations +localVariableDeclaration: DIM wsc? SHARED? wsc? variableDeclarationList; +staticVariableDeclaration: STATIC wsc variableDeclarationList; + +// 5.4.3.2 Local Constant Declarations +localConstDeclaration: constDeclaration; + +// 5.4.3.3 ReDim Statement +redimStatement: REDIM (wsc PRESERVE)? wsc? redimDeclarationList; +redimDeclarationList: redimVariableDcl (wsc? ',' wsc? redimVariableDcl)*; +// Had to add withExpression and memberAccess +// to match callStatement. +redimVariableDcl + : redimTypedVariableDcl + | redimUntypedDcl + | withExpressionDcl + | memberAccessExpressionDcl + ; +redimTypedVariableDcl: typedName wsc? dynamicArrayDim; +redimUntypedDcl: untypedName wsc? dynamicArrayClause; +withExpressionDcl: withExpression wsc? dynamicArrayDim; +memberAccessExpressionDcl: memberAccessExpression wsc? dynamicArrayDim; +dynamicArrayDim: '(' wsc? dynamicBoundsList wsc? ')'; +dynamicBoundsList: dynamicDimSpec (wsc? ',' wsc? dynamicDimSpec)*; +dynamicDimSpec: (dynamicLowerBound wsc)? dynamicUpperBound; +dynamicLowerBound: integerExpression wsc? TO; +dynamicUpperBound: integerExpression; +dynamicArrayClause: dynamicArrayDim wsc? asClause?; + +// 5.4.3.4 Erase Statement +eraseStatement: ERASE wsc? eraseList; +eraseList: eraseElement (wsc? ',' wsc? eraseElement)*; +eraseElement: lExpression; + +// 5.4.3.5 Mid/MidB/Mid$/MidB$ Statement +midStatement: modeSpecifier wsc? '(' wsc? stringArgument wsc? ',' wsc? startMid wsc? (',' wsc? length)? ')' wsc? EQ wsc? expression; +modeSpecifier + : MID + | MIDB + | MID_D + | MIDB_D + ; +stringArgument: boundVariableExpression; +// Changed name from start to startMid due to a problem with the Dart compilation. +startMid: integerExpression; +length: integerExpression; + +// 5.4.3.6 LSet Statement +lsetStatement: LSET wsc? boundVariableExpression wsc? EQ wsc? expression; + +// 5.4.3.7 RSet Statement +rsetStatement: RSET wsc? boundVariableExpression wsc? EQ wsc? expression; + +// 5.4.3.8 Let Statement +letStatement: (LET wsc)? lExpression wsc? EQ wsc? expression; + +// 5.4.3.9 Set Statement +setStatement: SET wsc lExpression wsc? EQ wsc? expression; + +// 5.4.4 Error Handling Statements +errorHandlingStatement + : onErrorStatement + | resumeStatement + | errorStatement + ; + +// 5.4.4.1 On Error Statement +onErrorStatement: ON wsc ERROR wsc? errorBehavior; +errorBehavior + : RESUME wsc NEXT + | GOTO wsc? statementLabel + ; + +// 5.4.4.2 Resume Statement +resumeStatement: RESUME wsc? (NEXT| statementLabel)?; + +// 5.4.4.3 Error Statement +errorStatement: ERROR wsc errorNumber; +errorNumber: integerExpression; + +// 5.4.5 File Statements +fileStatement + : openStatement + | closeStatement + | seekStatement + | lockStatement + | unlockStatement + | lineInputStatement + | widthStatement + | printStatement + | writeStatement + | inputStatement + | putStatement + | getStatement + ; + +// 5.4.5.1 Open Statement +openStatement + : OPEN wsc? pathName wsc? modeClause? wsc accessClause? wsc? lock? wsc? AS wsc? fileNumber wsc? lenClause? + ; +pathName: expression; +modeClause: FOR wsc modeOpt; +modeOpt + : APPEND + | BINARY + | INPUT + | OUTPUT + | RANDOM + ; +accessClause: ACCESS wsc access; +access + : READ + | WRITE + | READ wsc WRITE + ; +lock + : SHARED + | LOCK wsc READ + | LOCK wsc WRITE + | LOCK wsc READ wsc WRITE + ; +lenClause: LEN wsc EQ wsc recLength; +recLength: expression; + +// 5.4.5.1.1 File Numbers +fileNumber + : markedFileNumber + | unmarkedFileNumber + ; +markedFileNumber: '#' expression; +unmarkedFileNumber: expression; + +// 5.4.5.2 Close and Reset Statements +closeStatement + : RESET + | CLOSE wsc? fileNumberList? + ; +fileNumberList: fileNumber (wsc? ',' wsc? fileNumber)*; + +// 5.4.5.3 Seek Statement +seekStatement: SEEK wsc fileNumber wsc? ',' wsc? position; +position: expression; + +// 5.4.5.4 Lock Statement +lockStatement: LOCK wsc fileNumber (wsc? ',' wsc? recordRange); +recordRange + : startRecordNumber + | startRecordNumber? wsc TO wsc endRecordNumber + ; +startRecordNumber: expression; +endRecordNumber: expression; + +// 5.4.5.5 Unlock Statement +unlockStatement: UNLOCK wsc fileNumber (wsc? ',' wsc? recordRange)?; + +// 5.4.5.6 Line Input Statement +lineInputStatement: LINE wsc INPUT wsc markedFileNumber wsc? ',' wsc? variableName; +variableName: variableExpression; + +// 5.4.5.7 Width Statement +widthStatement: WIDTH wsc markedFileNumber wsc? ',' wsc? lineWidth; +lineWidth: expression; + +// 5.4.5.8 Print Statement +printStatement: ((DEBUG | ME) '.')? PRINT wsc (markedFileNumber wsc? ',' wsc?)? outputList?; + +// 5.4.5.8.1 Output Lists +outputList: outputItem+; +outputItem + : outputClause charPosition? + | charPosition; +outputClause: spcClause | tabClause| outputExpression; +charPosition: ';' | ',' | wsc; +outputExpression: expression; +spcClause: SPC wsc '(' wsc? spcNumber wsc? ')'; +spcNumber: expression; +tabClause: TAB wsc '(' wsc? tabNumber wsc? ')'; +tabNumber: expression; + +// 5.4.5.9 Write Statement +writeStatement: WRITE wsc markedFileNumber wsc? ',' wsc? outputList?; + +// 5.4.5.10 Input Statement +inputStatement: INPUT wsc markedFileNumber wsc? ',' wsc? inputList; +inputList: inputVariable (wsc? ',' wsc? inputVariable)*; +inputVariable: boundVariableExpression; + +// 5.4.5.11 Put Statement +putStatement: PUT wsc fileNumber wsc? ',' wsc? recordNumber? wsc? ',' wsc? data; +recordNumber: expression; +data: expression; + +// 5.4.5.12 Get Statement +getStatement: GET wsc fileNumber wsc? ',' wsc? recordNumber? wsc? ',' wsc? variable; +variable: variableExpression; + +// Attribute Statement +attributeStatement + : ATTRIBUTE WS ambiguousIdentifier '.' attributeDescName WS? EQ WS? STRINGLITERAL + | ATTRIBUTE WS ambiguousIdentifier '.' attributeUsrName WS? EQ WS? '-'? INTEGERLITERAL + | ATTRIBUTE WS ambiguousIdentifier '.' VB_PROCDATA '.' VB_INVOKE_FUNC WS EQ WS STRINGLITERAL + ; + +attributeDescName + : VB_DESCRIPTION + | VB_VARDESCRIPTION + | VB_MEMBERFLAGS + | VB_VARMEMBERFLAGS + ; + +attributeUsrName + : 'VB_USERMEMID' + | 'VB_VARUSERMEMID' + ; + +//--------------------------------------------------------------------------------------- +// 5.6 Expressions +// Modifying the order will affect the order of operations +// valueExpression must be rolled up into expression due to mutual left recursion +// operatorExpression must be rolled up into expression due to mutual left recursion +// memberAccess +// DictionaryAccess +// The name 'newExpression' fails under the Go language +expression + : literalExpression # LiteralExpress + | parenthesizedExpression # ParenthesizedExpress + | typeofIsExpression # TypeofIsExpress + | NEW wsc? expression # NewExpress + | expression wsc? POW wsc? expression # ArithmeticExpression + | MINUS wsc? expression # UnaryMinusExpression + | expression wsc? (DIV | MULT) wsc? expression # ArithmeticExpression + | expression wsc? MOD wsc? expression # ArithmeticExpression + | expression wsc? (PLUS | MINUS) wsc? expression # ArithmeticExpression + | expression wsc? AMPERSAND wsc? expression # ConcatExpression + | expression wsc? (IS | LIKE | GEQ | LEQ | GT | LT | NEQ | EQ) wsc? expression # RelationExpression + | NOT wsc? expression # NotOperatorExpression + | expression wsc? (AND | OR | XOR | EQV | IMP) wsc? expression # BooleanExpress + | lExpression # LExpress + ; + +// Several of the lExpression rules are rolled up due to Mutual Left Recursion +// Many are also listed separately due to their specific use elsewhere. +lExpression + : simpleNameExpression #simpleNameExpress + | ME #instanceExpression + | lExpression '.' wsc? unrestrictedName #memberAccessExpress + | lExpression wsc? LINE_CONTINUATION wsc?'.' wsc? unrestrictedName #memberAccessExpress + | lExpression wsc? '(' wsc? argumentList wsc? ')' #indexExpress + | lExpression '!' unrestrictedName #dictionaryAccessExpress + | lExpression wsc? LINE_CONTINUATION wsc? '!' unrestrictedName #dictionaryAccessExpress + | lExpression wsc? LINE_CONTINUATION wsc? '!' wsc? LINE_CONTINUATION wsc? unrestrictedName #dictionaryAccessExpress + | withExpression #withExpress + ; + +// 5.6.5 Literal Expressions +// check on hex and oct +// check definition of integer and float +literalExpression + : DATELITERAL + | FLOATLITERAL + | INTEGERLITERAL + | STRINGLITERAL + | literalIdentifier typeSuffix? + ; + +// 5.6.6 Parenthesized Expressions +parenthesizedExpression: LPAREN wsc? expression wsc? RPAREN; + +// 5.6.7 TypeOf…Is Expressions +typeofIsExpression: TYPEOF wsc? expression wsc? IS wsc? typeExpression; + +// 5.6.10 Simple Name Expressions +// Had to add reservedName and specialForm to allow calls to Abs() Debug. and Lbound() +simpleNameExpression + : name + | reservedName + | specialForm +; + +// 5.6.12 Member Access Expressions +// This expression is also rolled into lExpression. +// Changes here must be duplicated there +memberAccessExpression + : lExpression '.' wsc? unrestrictedName + | lExpression wsc? LINE_CONTINUATION wsc?'.' wsc? unrestrictedName + ; + +// 5.6.13 Index Expressions +// This expression is also rolled into lExpression. +// Changes here must be duplicated there +indexExpression + : lExpression wsc? '(' wsc? argumentList wsc? ')' + ; + +// 5.6.13.1 Argument Lists +argumentList: positionalOrNamedArgumentList?; +positionalOrNamedArgumentList + : (positionalArgument wsc? ',' wsc?)* requiredPositionalArgument + | (positionalArgument wsc? ',' wsc?)* namedArgumentList + ; +positionalArgument: argumentExpression?; +requiredPositionalArgument: argumentExpression; +namedArgumentList: namedArgument (wsc? ',' wsc? namedArgument)*; +namedArgument: unrestrictedName wsc? ASSIGN wsc? argumentExpression; +argumentExpression + : (BYVAL wsc)? expression + | addressofExpression + ; + +// 5.6.14 Dictionary Access Expressions +// This expression is also rolled into lExpression. +// Changes here must be duplicated there +dictionaryAccessExpression + : lExpression '!' unrestrictedName + | lExpression wsc? LINE_CONTINUATION wsc? '!' unrestrictedName + | lExpression wsc? LINE_CONTINUATION wsc? '!' wsc? LINE_CONTINUATION wsc? unrestrictedName + ; + +// 5.6.15 With Expressions +withExpression + : withMemberAccessExpression + | withDictionaryAccessExpression + ; +withMemberAccessExpression: '.' unrestrictedName; +withDictionaryAccessExpression: '!' unrestrictedName; + +// 5.6.16 Constrained Expressions +// The following Expressions have complex static requirements + +// 5.6.16.1 Constant Expressions +constantExpression: expression; + +// 5.6.16.2 Conditional Compilation Expressions +ccExpression: expression; + +// 5.6.16.3 Boolean Expressions +booleanExpression: expression; + +// 5.6.16.4 Integer Expressions +integerExpression: expression; + +// 5.6.16.5 +variableExpression: lExpression; + +// 5.6.16.6 +boundVariableExpression: lExpression; + +// 5.6.16.7 +typeExpression + : builtinType + | definedTypeExpression + ; +definedTypeExpression + : simpleNameExpression + | memberAccessExpression + ; + +// 5.6.16.8 +addressofExpression + : ADDRESSOF wsc procedurePointerExpression + ; +procedurePointerExpression + : simpleNameExpression + | memberAccessExpression + ; + +//--------------------------------------------------------------------------------------- +// Many of the following are labeled as tokens in the standard, but are parser rules here. +// 3.3.1 Separator and Special Tokens +// In theory whitespace should be ignored, but there are a handful of cases +// where statements MUST be at the beginning of a line or where a NO-WS +// rule appears in the parser rule. +// If may make things simpler here to send all wsc to the hidden channel +// and let a linting tool highlight the couple cases where whitespace +// will cause an error. +wsc: (WS | LINE_CONTINUATION)+; +// known as EOL in MS-VBAL +endOfLine + : wsc? (NEWLINE | commentBody | remStatement) wsc? + ; +// We usually don't care if a line of code begins with whitespace, and the parser rules are +// cleaner if we lump that in with the EOL or EOS "token". However, for those cases where +// something MUST occur on the start of a line, use endOfLineNoWs. +endOfLineNoWs + : wsc? (NEWLINE | commentBody | remStatement) + ; +// known as EOS in MS-VBAL +endOfStatement + : (endOfLine | wsc? COLON wsc?)+ + ; +endOfStatementNoWs + : (endOfLineNoWs | wsc? COLON)+ + ; +// The COMMENT token includes the leading single quote +commentBody: COMMENT; + +// 3.3.5.2 Reserved Identifiers and IDENTIFIER +reservedIdentifier + : statementKeyword + | markerKeyword + | operatorIdentifier + | specialForm + | reservedName + | reservedTypeIdentifier + | literalIdentifier + | remKeyword + | reservedForImplementationUse + | futureReserved + ; +// Known as IDENTIFIER in MS-VBAL +ambiguousIdentifier + : IDENTIFIER + | ambiguousKeyword + ; +statementKeyword + : CALL + | CASE + | CLOSE + | CONST + | DECLARE + | DEFBOOL + | DEFBYTE + | DEFCUR + | DEFDATE + | DEFDBL + | DEFINT + | DEFLNG + | DEFLNGLNG + | DEFLNGPTR + | DEFOBJ + | DEFSNG + | DEFSTR + | DEFVAR + | DIM + | DO + | ELSE + | ELSEIF + | END + | ENDIF + | ENUM + | ERASE + | EVENT + | EXIT + | FOR + | FRIEND + | FUNCTION + | GET + | GLOBAL + | GOSUB + | GOTO + | IF + | IMPLEMENTS + | INPUT + | LET + | LOCK + | LOOP + | LSET + | NEXT + | ON + | OPEN + | OPTION + | PRINT + | PRIVATE + | PUBLIC + | PUT + | RAISEEVENT + | REDIM + | RESUME + | RETURN + | RSET + | SEEK + | SELECT + | SET + | STATIC + | STOP + | SUB + | TYPE + | UNLOCK + | WEND + | WHILE + | WITH + | WRITE + ; +remKeyword: REM; +markerKeyword + : ANY + | AS + | BYREF + | BYVAL + | CASE + | EACH + | ELSE + | IN + | NEW + | SHARED + | UNTIL + | WITHEVENTS + | WRITE + | OPTIONAL + | PARAMARRAY + | PRESERVE + | SPC + | TAB + | THEN + | TO + ; +operatorIdentifier + : ADDRESSOF + | AND + | EQV + | IMP + | IS + | LIKE + | NEW + | MOD + | NOT + | OR + | TYPEOF + | XOR + ; +reservedName + : ABS + | CBOOL + | CBYTE + | CCUR + | CDATE + | CDBL + | CDEC + | CINT + | CLNG + | CLNGLNG + | CLNGPTR + | CSNG + | CSTR + | CVAR + | CVERR + | DATE + | DEBUG + | DOEVENTS + | FIX + | INT + | LEN + | LENB + | ME + | PSET + | SCALE + | SGN + | STRING + ; +specialForm + : ARRAY + | CIRCLE + | INPUT + | INPUTB + | LBOUND + | SCALE + | UBOUND + ; +reservedTypeIdentifier + : BOOLEAN + | BYTE + | CURRENCY + | DATE + | DOUBLE + | INTEGER + | LONG + | LONGLONG + | LONGPTR + | SINGLE + | STRING + | VARIANT + ; + +// If we did not scoop up the bracketed forms in the Lexer, they would have become +// Foreign Names. +reservedTypeIdentifierB + : BOOLEAN_B + | BYTE_B + | CURRENCY_B + | DATE_B + | DOUBLE_B + | INTEGER_B + | LONG_B + | LONGLONG_B + | LONGPTR_B + | SINGLE_B + | STRING_B + | VARIANT_B + ; + +typeableReservedName + : DATE + | STRING + ; +literalIdentifier + : booleanLiteralIdentifier + | objectLiteralIdentifier + | variantLiteralIdentifier + ; +booleanLiteralIdentifier + : TRUE + | FALSE + ; +objectLiteralIdentifier + : NOTHING + ; +variantLiteralIdentifier + : EMPTY_X + | NULL_ + ; +reservedForImplementationUse + : ATTRIBUTE + | LINEINPUT + | VB_BASE + | VB_CONTROL + | VB_CREATABLE + | VB_CUSTOMIZABLE + | VB_DESCRIPTION + | VB_EXPOSED + | VB_EXT_KEY + | VB_GLOBALNAMESPACE + | VB_HELPID + | VB_INVOKE_FUNC + | VB_INVOKE_PROPERTY + | VB_INVOKE_PROPERTYPUT + | VB_INVOKE_PROPERTYPUTREF + | VB_MEMBERFLAGS + | VB_NAME + | VB_PREDECLAREDID + | VB_PROCDATA + | VB_TEMPLATEDERIVED + | VB_USERMEMID + | VB_VARDESCRIPTION + | VB_VARHELPID + | VB_VARMEMBERFLAGS + | VB_VARPROCDATA + | VB_VARUSERMEMID + ; +futureReserved + : CDECL + | DECIMAL + | DEFDEC + ; + +// 3.3.5.3 Special Identifier Forms + +// known as BUILTIN-TYPE in MS-VBAL +builtinType + : reservedTypeIdentifier + | reservedTypeIdentifierB + | OBJECT + | OBJECT_B + ; + +// Known as TYPED-NAME in MS-VBAL +// This probably could be turned into a token +typedName + : ambiguousIdentifier typeSuffix + | typeableReservedName typeSuffix + ; +typeSuffix + : '&' + | '%' + | '#' + | '!' + | '@' + | '$' + | '^' + ; + +//--------------------------------------------------------------------------------------- +// Extra Rules + +// lexer keywords not in the reservedIdentifier set. +// Any that are unused within the parser rules should probably +// be removed from the lexer. +ambiguousKeyword + : ACCESS + | ALIAS + | APPACTIVATE + | APPEND + | ASSERT + | BASE + | BEGIN + | BEGINPROPERTY + | BINARY + | CLASS + | CHDIR + | CHDRIVE + | CLASS_INITIALIZE + | CLASS_TERMINATE + | COLLECTION + | COMPARE + | DATABASE + | DELETESETTING + | ERROR + | ENDPROPERTY + | FILECOPY + | GO + | KILL + | LOAD + | LIB + | LINE + | MID + | MIDB + | MID_D + | MIDB_D + | MKDIR + | MODULE + | NAME + | OBJECT + | OUTPUT + | PROPERTY + | RANDOM + | RANDOMIZE + | READ + | RESET + | RMDIR + | SAVEPICTURE + | SAVESETTING + | SENDKEYS + | SETATTR + | STEP + | TEXT + | TIME + | UNLOAD + | VERSION + | WIDTH + ;