First commit

This commit is contained in:
Federico Aponte
2022-01-16 01:14:05 +01:00
commit 15320a8f46
48 changed files with 9608 additions and 0 deletions

116
CMakeLists.txt Normal file
View File

@@ -0,0 +1,116 @@
cmake_minimum_required(VERSION 3.10)
project(vb6_parser)
set(CMAKE_CXX_STANDARD 17)
#------------------------------------------------------------------------------
# see https://google.github.io/googletest/quickstart-cmake.html
include(FetchContent)
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip
)
# for Windows: prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
#------------------------------------------------------------------------------
add_compile_options($<$<CXX_COMPILER_ID:GNU,Clang>:-Wall> $<$<CXX_COMPILER_ID:GNU,Clang>:-Wextra>)
enable_testing()
#find_package(GTest CONFIG REQUIRED) # GoogleTest with vcpkg
find_package(Threads REQUIRED)
find_package(Boost REQUIRED QUIET COMPONENTS system)
add_library(vb6_parser_lib
src/raw_ast_printer.cpp
src/raw_ast_printer.hpp
src/color_console.cpp
src/color_console.hpp
src/cpp_ast_printer.cpp
src/cpp_ast_printer.hpp
src/vb6_ast.hpp
src/vb6_ast_adapt.hpp
src/vb6_config.hpp
src/vb6_error_handler.hpp
src/vb6_parser.cpp
src/vb6_parser.hpp
src/vb6_parser_def.hpp
src/vb6_parser_functions.cpp
src/vb6_parser_helper.cpp
src/vb6_parser_keywords.hpp
src/vb6_parser_operators.hpp
src/vb6_parser_statements.cpp
src/vb6_parser_statements_def.hpp
src/vb6_ast_printer.cpp
src/vb6_ast_printer.hpp
src/visual_basic_x3.hpp
)
target_compile_definitions(vb6_parser_lib
PRIVATE
-DBOOST_MPL_CFG_NO_PREPROCESSED_HEADERS
-DBOOST_MPL_LIMIT_LIST_SIZE=30
)
add_executable(vb6_parser
src/vb6_parser_main.cpp
src/vb6_test1.cpp
src/vb6_test2.cpp
)
target_compile_definitions(vb6_parser
PRIVATE
-DBOOST_MPL_CFG_NO_PREPROCESSED_HEADERS
-DBOOST_MPL_LIMIT_LIST_SIZE=30
)
target_link_libraries(vb6_parser
PRIVATE
vb6_parser_lib
Boost::system
Threads::Threads
)
# ---- test
add_executable(vb6_parser_test
src/test_gosub.cpp
src/test_grammar_helper.hpp
src/vb6_parser_statements_test.cpp
src/vb6_parser_test.cpp
src/vb6_parser_test_main.cpp
)
target_compile_definitions(vb6_parser_test
PRIVATE
-DBOOST_MPL_CFG_NO_PREPROCESSED_HEADERS
-DBOOST_MPL_LIMIT_LIST_SIZE=30
)
target_link_libraries(vb6_parser_test
PRIVATE
vb6_parser_lib
gtest_main
Boost::system
Threads::Threads
)
#[[
# GoogleTest with vcpkg, probably not a good idea
target_link_libraries(vb6_parser_test
PRIVATE
vb6_parser_lib
GTest::gtest
GTest::gtest_main
GTest::gmock
GTest::gmock_main
Boost::system
Threads::Threads
)
#]]
include(GoogleTest)
gtest_discover_tests(vb6_parser_test)
#add_test(AllTestsInMain vb6_parser_test)

26
README.md Normal file
View File

@@ -0,0 +1,26 @@
# vb6_parser
A parsing engine for Microsoft's Visual Basic 6 programming language.
## Introduction
This is a parser for the [Visual Basic 6](https://en.wikipedia.org/wiki/Visual_Basic) programming language, implemented using the [Boost Spirit x3](https://www.boost.org/doc/libs/develop/libs/spirit/doc/x3/html/index.html) library.
https://github.com/fedapo/experiments.git
## Building
## Usage
## History
## Acknowledgements
- Boost Spirit X3
- GTest
- CMake
- GCC
- Clang
- MSYS2
- Vcpkg
- The C++ Committee

131
data/long_source.bas Normal file
View File

@@ -0,0 +1,131 @@
Attribute ModuleName = "MyForm"
Attribute ProgID = "ca-fe-de-ad"
Option Explicit
Option Compare Text
Option Compare Binary
' first comment
Rem second comment
' long_source.bas
Dim g_logger0
Dim a_long As Long
Const str As String = "una stringa"
Const a_bool = True
Const an_object = Nothing
' declaration of global constants with no type
Const an_integer = 1234%, a_long = 1234&, a_hex_long = &Hcafedead&, an_oct_long = &01234&, an_integer2 = 1234
Const a_single_float = 1234!, a_double_float = 1234#, a_float = 2.8
' declaration of global variables with type
Dim g_logger As Long
Dim name As New Form
Type PatRecord
name As String
age As Integer
End Type
Enum PatTypes
inpatient
outpatient
End Enum
Global g_logger As Long, v1, XRes As New Object, ptr As Module.MyRec, g_active As Boolean
Const e As Single = 2.8, pi As Double = 3.14, u As Integer = -1
Private Const PI As Double = 3.1415
Private Declare Sub BeepVB Lib "kernel32.dll" Alias "Beep" (ByVal time As Long, ByVal xx As Single)
Private Declare Function BeepVB Lib "kernel32.dll" Alias "Beep" (ByVal time As Long, ByVal xx As Single) As Long
Const e As Double = 2.8, pi As Double = 3.14, u As Integer = -1
Global g_logger As Long, v1, XRes As Object, ptr As MyRec, g_active As Boolean
Private Declare Sub PFoo Lib "mylib.dll" Alias "PFoo" (ByVal val As Long)
Enum MyEnum1
c1 = 0
c2 = 1
End Enum
Public Type MyRecord1
v1 As String
v2 As Long
End Type
Public Event OnChange(ByVal Text As String)
Sub foo(Optional ByVal name As String = "pippo")
End Sub
Sub foo(ByVal name As String, ByRef val As Integer)
Dim x As Long
x = 4
End Sub
Private Function OneFunc(ByVal name As String, ByRef val As Integer) As Integer
End Function
Private Function NoParamFunc() As Object
End Function
Private Sub my_sub(ByRef str As String, ByVal valid As Boolean)
End Sub
Private Sub my_sub(ByRef str As String, ByVal valid As Boolean, Optional ByVal flag As Boolean = True)
var1.func().pnt1.X = 34
End Sub
Private Sub my_sub(ByRef str As String, Optional ByVal valid As Boolean = false)
' This is comment line 1
' Comment line 2
Set var1 = " ciao\"
Let var2 = 54.7
var3 = Func("descr", 54.7)
str = CStr(i)
Dim var1 As String, var2 As Integer
ReDim var1(15)
Exit Function
GoSub label1
On Error Resume Next
label1:
Call Foo(13)
Foo "Sea", Nothing, False
RaiseEvent OnChange("hi!")
With obj
' we miss the dot notation for subroutines and functions
'Call Module1.foo(True, .Name)
Call foo(True, .Name)
End With
End Sub
Sub Func(ByRef obj As Canvas)
Dim i As Integer
i = 23
Dim pnt As Point3d
pnt.x = 1.0
pnt.y = 1.2
' we miss the dot notation for subroutines and functions
'Call obj.Plot(pnt)
While cond(34, i)
Print str
For i = 1 To 100 Step 2
Dim str As String
str = CStr(i)
Call Print(str)
Next i
Wend
End Sub

39
data/prova_form.frm Normal file
View File

@@ -0,0 +1,39 @@
VERSION 5.00
Begin VB.Form prova_form
Caption = "prova_form"
ClientHeight = 3030
ClientLeft = 120
ClientTop = 450
ClientWidth = 4560
LinkTopic = "Form1"
ScaleHeight = 3030
ScaleWidth = 4560
StartUpPosition = 3 'Windows Default
Begin VB.CommandButton Command1
Caption = "Command1"
Height = 375
Index = 0
Left = 360
TabIndex = 0
Top = 240
Width = 1095
End
End
Attribute VB_Name = "prova_form"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Option Explicit
Public id As Integer
Private Sub Command1_Click(Index As Integer)
End Sub
Private Sub Form_Load()
Debug.Print "== prova_form.Load =="
Debug.Print id
End Sub

99
data/prova_module.bas Normal file
View File

@@ -0,0 +1,99 @@
Attribute VB_Name = "prova_module"
Option Explicit
Enum MyEnum1
v0 = False ' this is ok only because it can be cast to an integer
v1 = 1
v2 = "2" ' this is ok only because it can be cast to an integer
v3
End Enum
Type MyRec1
f1 As Integer
f2 As String
End Type
Type MyRec2
f1 As Integer
f2 As String
f3 As MyRec1
End Type
Private myprop As String
Property Get prova_property() As String
prova_property = myprop
End Property
Property Let prova_property(v As String) ' using "Let" because it is a built-in type?
myprop = v
End Property
Sub prova_data_member_access()
Debug.Print "== prova_data_member_access =="
Dim obj1 As prova_module.MyRec1
obj1.f1 = 45
obj1.f2 = "prova"
Dim obj2 As prova_module.MyRec2
obj2.f1 = 1
obj2.f2 = "xxx"
obj2.f3.f1 = 2
obj2.f3.f2 = "yyy"
End Sub
Function func1() As Integer
Debug.Print "== func1 =="
func1 = 45
End Function
Function func2() As prova_module.MyRec1
Debug.Print "== func2 =="
func2.f1 = 45
func2.f2 = "prova"
End Function
Sub Main()
prova_data_member_access
Dim frm As prova_project.prova_form
Set frm = New prova_form
frm.id = 345
VB.Global.Load frm.Command1(1) ' loads a form or control into memory
frm.Command1(1).Caption = "Nuovo"
frm.Command1(1).Top = 750
frm.Command1(1).Left = 750
frm.Command1(1).Width = 1000
frm.Command1(1).Height = 400
frm.Command1(1).Visible = True
frm.Command1(1).Enabled = True
'VB.Global.Load frm ' loads a form or control into memory
frm.Show
func1
' Compile Error: Expected Sub, Function, or Property
'func2.f1
'func2().f1
Debug.Print func2.f1
Debug.Print func2().f2
Debug.Print MyEnum1.v0
Debug.Print MyEnum1.v1
Debug.Print MyEnum1.v2
Debug.Print MyEnum1.v3
prova_property = "xyxy"
Debug.Print prova_property
prova_form.Show
End Sub

34
data/prova_project.vbp Normal file
View File

@@ -0,0 +1,34 @@
Type=Exe
Form=prova_form.frm
Reference=*\G{00020430-0000-0000-C000-000000000046}#2.0#0#C:\Windows\SysWOW64\stdole2.tlb#OLE Automation
Module=prova_module; prova_module.bas
IconForm="prova_form"
Startup="Sub Main"
HelpFile=""
Title="prova_project"
Command32=""
Name="prova_project"
HelpContextID="0"
CompatibleMode="0"
MajorVer=1
MinorVer=0
RevisionVer=0
AutoIncrementVer=0
ServerSupportFiles=0
VersionCompanyName="csh"
CompilationType=0
OptimizationType=0
FavorPentiumPro(tm)=0
CodeViewDebugInfo=0
NoAliasing=0
BoundsCheck=0
OverflowCheck=0
FlPointCheck=0
FDIVCheck=0
UnroundedFP=0
StartMode=0
Unattended=0
Retained=0
ThreadPerObject=0
MaxNumberOfThreads=1
DebugStartupOption=0

65
data/test.bas Normal file
View File

@@ -0,0 +1,65 @@
Attribute ModuleName = "MyForm"
Attribute ProgID = "a1-b2-c3-d4"
' test.bas
Option Explicit
' compare options
Option Compare Text
Option Compare Binary
' array base options
Option Base 0
Option Base 1
' test.bas
Global g_v1 As String, g_v2 As Integer
Public g_v3 As Long
Private g_v4 As Single
' we miss the declaration of arrays
'Dim arr0() As String
'Dim arr1(10) As Integer
'Dim arr2(10, 10) As Long
' what about this?
'Dim values As Integer * 10
Enum Colors
Black = 0
White = 1
Blue = 2
Red = 3
Green = 4
End Enum
Public Type Point3d
x As Double
y As Double
End Type
Sub Func(ByRef obj As Canvas)
Dim i As Integer
i = 23
Dim pnt As Point3d
pnt.x = 1.0
pnt.y = 1.2
' we miss the dot notation for subroutines and functions
'Call obj.Plot(pnt)
If check(87) Then
Print "check1"
ElseIf check(92) Then
Print "check2"
Else
Print "check3"
End If
While cond(34, i)
Print str
For i = 1 To 100 Step 2
Dim str As String
str = CStr(i)
Call Print(str)
Next i
Wend
End Sub

View File

@@ -0,0 +1,89 @@
AST ELEMENT GRAMMAR RULE
----------------------------------------------------------------------
empty_line empty_line
lonely_comment lonely_comment
quoted_string quoted_string
var_identifier basic_identifier
identifier_context identifier_context
type_identifier
vb6_ast::variable vb6_grammar::single_var_declaration
decorated_variable decorated_variable
vb6_ast::global_var_decls vb6_grammar::global_var_declaration
integer_dec
integer_hex
integer_oct
long_dec
long_hex
long_oct
vb6_ast::const_expr vb6_grammar::const_expression
vb6_ast::const_var
vb6_ast::const_var_stat vb6_grammar::const_var_declaration
vb6_ast::record vb6_grammar::record_declaration
vb6_ast::enum_item
vb6_ast::vb_enum vb6_grammar::enum_declaration
expression expression
func_call functionCall
vb6_ast::func_param vb6_grammar::param_decl
vb6_ast::external_decl vb6_grammar::
vb6_ast::subHead vb6_grammar::subHead
eventHead vb6_grammar::eventHead
vb6_ast::functionHead vb6_grammar::functionHead
propertyLetHead vb6_grammar::property_letHead
propertySetHead vb6_grammar::property_setHead
propertyGetHead vb6_grammar::property_getHead
vb6_ast::externalSub vb6_grammar::external_sub_decl
vb6_ast::externalFunction vb6_grammar::external_function_decl
vb6_ast::assignStatement vb6_grammar::
localVarDeclStat localvardeclStatement
redimStatement redimStatement
exitStatement exitStatement
gotoStatement gotoStatement
onerrorStatement onerrorStatement
resumeStatement resumeStatement
labelStatement labelStatement
callStatement callimplicitStatement
callStatement callexplicitStatement
raiseeventStatement raiseeventStatement
vb6_ast::whileStatement vb6_grammar::whileStatement
vb6_ast::doStatement vb6_grammar::doStatement
vb6_ast::dowhileStatement vb6_grammar::dowhileStatement
vb6_ast::loopwhileStatement vb6_grammar::loopwhileStatement
vb6_ast::dountilStatement vb6_grammar::dountilStatement
vb6_ast::loopuntilStatement vb6_grammar::loopuntilStatement
vb6_ast::forStatement vb6_grammar::forStatement
vb6_ast::foreachStatement vb6_grammar::foreachStatement
if_branch
vb6_ast::ifelseStatement vb6_grammar::ifelseStatement
vb6_ast::withStatement vb6_grammar::withStatement
case_relational_expr
case_block case_block
vb6_ast::selectStatement vb6_grammar::selectStatement
vb6_ast::if_branch ifBranch
vb6_ast::if_branch elsifBranch
vb6_ast::statement_block elseBranch
vb6_ast::singleStatement vb6_grammar::singleStatement
vb6_ast::statement_block vb6_grammar::statement_block
vb6_ast::subDef vb6_grammar::subDef
vb6_ast::functionDef vb6_grammar::functionDef
vb6_ast::get_prop vb6_grammar::
vb6_ast::let_prop vb6_grammar::
vb6_ast::set_prop vb6_grammar::
STRICT_MODULE_STRUCTURE
module_attributes
option_block
declaration
functionList
vb_module
#vb6_ast::decl_item vb6_grammar::declaration
vb6_ast::declaration vb6_grammar::declaration
module_attribute
vb6_ast::vb_module

4
docs/notes_Spirit.md Normal file
View File

@@ -0,0 +1,4 @@
# Boost Spirit
https://stackoverflow.com/questions/38039237/parsing-identifiers-except-keywords \
https://www.codevamping.com/2018/09/identifier-parsing-in-boost-spirit-x3-custom-parser/

115
docs/notes_vb6.md Normal file
View File

@@ -0,0 +1,115 @@
# VB6 - Visual Basic 6
https://en.wikipedia.org/wiki/Visual_Basic_(classic) \
https://en.wikipedia.org/wiki/BASIC
BASIC = Beginner's All-purpose Symbolic Instruction Code
> At least for the people who send me mail about a new language that they're
> designing, the general advice is: do it to learn about how to write a compiler.
> Don't have any expectations that anyone will use it, unless you hook up with
> some sort of organization in a position to push it hard. It's a lottery, and
> some can buy a lot of the tickets. There are plenty of beautiful languages
> (more beautiful than C) that didn't catch on. But someone does win the lottery,
> and doing a language at least teaches you something. \
> _Dennis Ritchie (1941-2011)_ \
> _Creator of the C programming language and of Unix_
---
## Press
Visual Basic 6 Renewed to Run on Windows 8 \
https://www.infoq.com/news/2012/02/vb6_supported_on_win8
---
## Complete VB6 Grammars
ANTLR4-based Grammars
https://github.com/antlr/grammars-v4/tree/master/vb6
https://github.com/uwol/vb6parser \
https://github.com/uwol/vb6parser/blob/master/src/main/antlr4/io/proleap/vb6/VisualBasic6.g4
Grammar Zoo: http://slebok.github.io/zoo/index.html
http://boost.2283326.n4.nabble.com/Parser-operator-Difference-td4675788.html
---
## Expression Parsing
https://en.wikipedia.org/wiki/Operator-precedence_parser
---
## CodeProject Articles
Crafting an interpreter Part 1 - Parsing and Grammars \
https://www.codeproject.com/Articles/10115/Crafting-an-interpreter-Part-1-Parsing-and-Grammar \
Visual Basic 6.0: A giant more powerful than ever\
https://www.codeproject.com/Articles/710181/Visual-Basic-6-0-A-giant-more-powerful-than-ever
---
## Misc
**Grako** (grammar compiler) is a tool that takes grammars in a variation of EBNF as input, and outputs memoizing (Packrat) PEG parsers in Python. \
A generator of PEG/Packrat parsers from EBNF grammars. \
https://pypi.python.org/pypi/grako/
**TatSu** is a tool that takes grammars in a variation of EBNF as input, and outputs memoizing (Packrat) PEG parsers in Python. \
https://pypi.org/project/TatSu/
GOLD Parsing System \
http://goldparser.org/engine/1/vb6/index.htm \
http://goldparser.org/about/comparison-parsers.htm
http://www.eclipse.org/gmt/modisco/technologies/VisualBasic/#download
https://tomassetti.me/how-to-write-a-transpiler/
The vb2Py project is developing a suite of conversion tools to aid in translating Visual Basic projects into Python.\
http://vb2py.sourceforge.net/index.html
http://vb6awards.blogspot.com/2016/04/microsoft-update-or-open-source-vb6.html
---
## VB6 Named parameters
```vb
Function foo(Optional val1 = 1, Optional val2 = 2, Optional val3 = 3)
MsgBox "val1: " & val1 & " val2: " & val2 & " val3: " & val3
foo = val3
End Function
Private Sub Form_Load()
MsgBox "foo returned: " & foo(val3:=4)
End Sub
```
## VB6 Dot notation (x.y)
Access to members of
- Modules
- Classes, forms, controls
- Types
Types of members
- Data members
- Member functions
- Member subroutines
- Properties
```
module.variable
module.function
module.subroutine
module.enum
module.type
object.variable
object.function
object.subroutine
object.property
```

114
docs/vb6_attributes.md Normal file
View File

@@ -0,0 +1,114 @@
# VB6 Attributes
## Class (.cls)
```vb
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
Persistable = 0 'NotPersistable
DataBindingBehavior = 0 'vbNone
DataSourceBehavior = 0 'vbNone
MTSTransactionMode = 0 'NotAnMTSObject
END
Attribute VB_Name = "Interactive"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
...
```
## Form (.frm), Control (.ctl), Property Page (.pag)
```vb
VERSION 5.00
Object = "{8DDE6232-1BB0-11D0-81C3-0080C7A2EF7D}#3.0#0"; "flp32a30.ocx"
Begin VB.Form frmCalendar
...
```
```vb
VERSION 5.00
Object = "{8DDE6232-1BB0-11D0-81C3-0080C7A2EF7D}#3.0#0"; "flp32a30.ocx"
Begin VB.UserControl ConfSelection
...
```
```vb
VERSION 5.00
Object = "{8DDE6232-1BB0-11D0-81C3-0080C7A2EF7D}#3.0#0"; "flp32a30.ocx"
Begin VB.PropertyPage PropertyPage1
...
```
## Module (.bas)
```vb
Attribute VB_Name = "CENTSERV"
...
```
## Attributes
https://christopherjmcclellan.wordpress.com/2015/04/21/vb-attributes-what-are-they-and-why-should-we-use-them/
### Module Level Attributes
```vb
VB_Name = "Interactive"
VB_GlobalNameSpace = False
VB_Creatable = True
VB_PredeclaredId = False
VB_Exposed = False
```
### Other Attributes
There are also a number of attributes that can be applied to module variables (fields), properties, and procedures.
| Attribute | Description |
| --------- | ----------- |
|VB_VarUserMemId| Determines the order of the variables in the Object Broswer. A value of 0 (zero) declares the variable to be the default member of the class.|
|VB_VarDescription| The value of this attribute will be displayed in the Object Broswer.|
|VB_UserMemId| |
VB_Description| |
```vb
LmoIEIDXRad.VB_VarHelpID = -1
Item.VB_UserMemId = 0
NewEnum.VB_UserMemId = -4
```
There is one more special value for `VB_UserMemId` and that value is -4.
Negative 4 always indicates that the function being marked should return
a [_NewEnum] enumerator.
```vb
' Header
Attribute VB_Name = "ClassOrModuleName"
Attribute VB_GlobalNameSpace = False ' ignored
Attribute VB_Creatable = False ' ignored
Attribute VB_PredeclaredId = False ' a Value of True creates a default global instance
Attribute VB_Exposed = True ' Controls how the class can be instanced.
' Module Scoped Variables
Attribute variableName.VB_VarUserMemId = 0 ' Zero indicates that this is the default member of the class.
Attribute variableName.VB_VarDescription = "some string" ' Adds the text to the Object Browser information for this variable.
' Procedures
Attribute procName.VB_Description = "some string" ' Adds the text to the Object Browser information for the procedure.
Attribute procName.VB_UserMemId = someInteger
' 0: Makes the function the default member of the class.
' -4: Specifies that the function returns an Enumerator.
```
In a control.
```vb
Public Sub SpeechMarkBeg()
Attribute SpeechMarkBeg.VB_MemberFlags = "40"
Public Property Get MaxLength() As Single
Attribute MaxLength.VB_Description = "When the length of the dictation hits this number (in minutes) then the system stops recording and asks the user if he/she wants to proceed."
```

57
msvc/vb6_parser.sln Normal file
View File

@@ -0,0 +1,57 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.28701.123
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vb6_parser", "vb6_parser.vcxproj", "{D252565E-F568-11E9-B82C-5AA538984BD8}"
ProjectSection(ProjectDependencies) = postProject
{6808F98C-3361-447B-8F34-F78EFB332316} = {6808F98C-3361-447B-8F34-F78EFB332316}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vb6_parser_test", "vb6_parser_test.vcxproj", "{B5CDF59F-23D6-4E11-A5AE-F1FEDD1C8F58}"
ProjectSection(ProjectDependencies) = postProject
{6808F98C-3361-447B-8F34-F78EFB332316} = {6808F98C-3361-447B-8F34-F78EFB332316}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vb6_parser_lib", "vb6_parser_lib.vcxproj", "{6808F98C-3361-447B-8F34-F78EFB332316}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{D252565E-F568-11E9-B82C-5AA538984BD8}.Debug|x64.ActiveCfg = Debug|x64
{D252565E-F568-11E9-B82C-5AA538984BD8}.Debug|x64.Build.0 = Debug|x64
{D252565E-F568-11E9-B82C-5AA538984BD8}.Debug|x86.ActiveCfg = Debug|Win32
{D252565E-F568-11E9-B82C-5AA538984BD8}.Debug|x86.Build.0 = Debug|Win32
{D252565E-F568-11E9-B82C-5AA538984BD8}.Release|x64.ActiveCfg = Release|x64
{D252565E-F568-11E9-B82C-5AA538984BD8}.Release|x64.Build.0 = Release|x64
{D252565E-F568-11E9-B82C-5AA538984BD8}.Release|x86.ActiveCfg = Release|Win32
{D252565E-F568-11E9-B82C-5AA538984BD8}.Release|x86.Build.0 = Release|Win32
{B5CDF59F-23D6-4E11-A5AE-F1FEDD1C8F58}.Debug|x64.ActiveCfg = Debug|x64
{B5CDF59F-23D6-4E11-A5AE-F1FEDD1C8F58}.Debug|x64.Build.0 = Debug|x64
{B5CDF59F-23D6-4E11-A5AE-F1FEDD1C8F58}.Debug|x86.ActiveCfg = Debug|Win32
{B5CDF59F-23D6-4E11-A5AE-F1FEDD1C8F58}.Debug|x86.Build.0 = Debug|Win32
{B5CDF59F-23D6-4E11-A5AE-F1FEDD1C8F58}.Release|x64.ActiveCfg = Release|x64
{B5CDF59F-23D6-4E11-A5AE-F1FEDD1C8F58}.Release|x64.Build.0 = Release|x64
{B5CDF59F-23D6-4E11-A5AE-F1FEDD1C8F58}.Release|x86.ActiveCfg = Release|Win32
{B5CDF59F-23D6-4E11-A5AE-F1FEDD1C8F58}.Release|x86.Build.0 = Release|Win32
{6808F98C-3361-447B-8F34-F78EFB332316}.Debug|x64.ActiveCfg = Debug|x64
{6808F98C-3361-447B-8F34-F78EFB332316}.Debug|x64.Build.0 = Debug|x64
{6808F98C-3361-447B-8F34-F78EFB332316}.Debug|x86.ActiveCfg = Debug|Win32
{6808F98C-3361-447B-8F34-F78EFB332316}.Debug|x86.Build.0 = Debug|Win32
{6808F98C-3361-447B-8F34-F78EFB332316}.Release|x64.ActiveCfg = Release|x64
{6808F98C-3361-447B-8F34-F78EFB332316}.Release|x64.Build.0 = Release|x64
{6808F98C-3361-447B-8F34-F78EFB332316}.Release|x86.ActiveCfg = Release|Win32
{6808F98C-3361-447B-8F34-F78EFB332316}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {559721B8-C7D6-4E7F-8089-9F93DCD9C915}
EndGlobalSection
EndGlobal

154
msvc/vb6_parser.vcxproj Normal file
View File

@@ -0,0 +1,154 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{D252565E-F568-11E9-B82C-5AA538984BD8}</ProjectGuid>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\..\..\miscellaneous.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\..\..\miscellaneous.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\..\..\miscellaneous.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\..\..\miscellaneous.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<PreprocessorDefinitions>BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS;BOOST_MPL_LIMIT_LIST_SIZE=30;WIN32;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<Optimization>Disabled</Optimization>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<TargetMachine>MachineX86</TargetMachine>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS;BOOST_MPL_LIMIT_LIST_SIZE=30;WIN32;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<TargetMachine>MachineX86</TargetMachine>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PreprocessorDefinitions>BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS;BOOST_MPL_LIMIT_LIST_SIZE=30;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS;BOOST_MPL_LIMIT_LIST_SIZE=30;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\src\color_console.cpp" />
<ClCompile Include="..\src\vb6_parser_main.cpp" />
<ClCompile Include="..\src\test_gosub.cpp" />
<ClCompile Include="..\src\vb6_test1.cpp" />
<ClCompile Include="..\src\vb6_test2.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\src\color_console.hpp" />
<ClInclude Include="..\src\test_grammar_helper.hpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="vb6_parser_lib.vcxproj">
<Project>{6808f98c-3361-447b-8f34-f78efb332316}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{1BD2A6B2-F569-11E9-802A-5AA538984BD8}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{4319F8BA-F569-11E9-802A-5AA538984BD8}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{2E09A9E8-F569-11E9-B82C-5AA538984BD8}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\src\color_console.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\test_gosub.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\vb6_parser_main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\vb6_test2.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\vb6_test1.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\src\color_console.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\src\test_grammar_helper.hpp">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

180
msvc/vb6_parser_lib.vcxproj Normal file
View File

@@ -0,0 +1,180 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\src\cpp_ast_printer.cpp" />
<ClCompile Include="..\src\raw_ast_printer.cpp" />
<ClCompile Include="..\src\vb6_ast_printer.cpp" />
<ClCompile Include="..\src\vb6_parser.cpp" />
<ClCompile Include="..\src\vb6_parser_functions.cpp" />
<ClCompile Include="..\src\vb6_parser_helper.cpp" />
<ClCompile Include="..\src\vb6_parser_statements.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\src\cpp_ast_printer.hpp" />
<ClInclude Include="..\src\raw_ast_printer.hpp" />
<ClInclude Include="..\src\vb6_ast.hpp" />
<ClInclude Include="..\src\vb6_ast_adapt.hpp" />
<ClInclude Include="..\src\vb6_ast_printer.hpp" />
<ClInclude Include="..\src\vb6_config.hpp" />
<ClInclude Include="..\src\vb6_error_handler.hpp" />
<ClInclude Include="..\src\vb6_parser.hpp" />
<ClInclude Include="..\src\vb6_parser_def.hpp" />
<ClInclude Include="..\src\vb6_parser_keywords.hpp" />
<ClInclude Include="..\src\vb6_parser_operators.hpp" />
<ClInclude Include="..\src\vb6_parser_statements_def.hpp" />
<ClInclude Include="..\src\visual_basic_x3.hpp" />
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{6808f98c-3361-447b-8f34-f78efb332316}</ProjectGuid>
<RootNamespace>vb6parserlib</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\..\..\miscellaneous.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\..\..\miscellaneous.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\..\..\miscellaneous.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\..\..\miscellaneous.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS;BOOST_MPL_LIMIT_LIST_SIZE=30;WIN32;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>
</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS;BOOST_MPL_LIMIT_LIST_SIZE=30;WIN32;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>
</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS;BOOST_MPL_LIMIT_LIST_SIZE=30;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>
</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS;BOOST_MPL_LIMIT_LIST_SIZE=30;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>
</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="Source Files\ast_printers">
<UniqueIdentifier>{4db10dc1-bd75-48ec-a340-cd63bfe93a54}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\parser">
<UniqueIdentifier>{5fc50242-d0ab-4904-a16b-20faa38be067}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\ast_printers">
<UniqueIdentifier>{58be8f26-3ece-4851-9850-256d9c32c62e}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\parser">
<UniqueIdentifier>{017cf1f3-4296-4f1b-b845-08fbee14fac0}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\src\cpp_ast_printer.cpp">
<Filter>Source Files\ast_printers</Filter>
</ClCompile>
<ClCompile Include="..\src\raw_ast_printer.cpp">
<Filter>Source Files\ast_printers</Filter>
</ClCompile>
<ClCompile Include="..\src\vb6_ast_printer.cpp">
<Filter>Source Files\ast_printers</Filter>
</ClCompile>
<ClCompile Include="..\src\vb6_parser.cpp">
<Filter>Source Files\parser</Filter>
</ClCompile>
<ClCompile Include="..\src\vb6_parser_functions.cpp">
<Filter>Source Files\parser</Filter>
</ClCompile>
<ClCompile Include="..\src\vb6_parser_helper.cpp">
<Filter>Source Files\parser</Filter>
</ClCompile>
<ClCompile Include="..\src\vb6_parser_statements.cpp">
<Filter>Source Files\parser</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\src\cpp_ast_printer.hpp">
<Filter>Header Files\ast_printers</Filter>
</ClInclude>
<ClInclude Include="..\src\raw_ast_printer.hpp">
<Filter>Header Files\ast_printers</Filter>
</ClInclude>
<ClInclude Include="..\src\vb6_ast_printer.hpp">
<Filter>Header Files\ast_printers</Filter>
</ClInclude>
<ClInclude Include="..\src\vb6_config.hpp">
<Filter>Header Files\parser</Filter>
</ClInclude>
<ClInclude Include="..\src\vb6_error_handler.hpp">
<Filter>Header Files\parser</Filter>
</ClInclude>
<ClInclude Include="..\src\vb6_parser.hpp">
<Filter>Header Files\parser</Filter>
</ClInclude>
<ClInclude Include="..\src\vb6_parser_def.hpp">
<Filter>Header Files\parser</Filter>
</ClInclude>
<ClInclude Include="..\src\vb6_parser_keywords.hpp">
<Filter>Header Files\parser</Filter>
</ClInclude>
<ClInclude Include="..\src\vb6_parser_operators.hpp">
<Filter>Header Files\parser</Filter>
</ClInclude>
<ClInclude Include="..\src\vb6_parser_statements_def.hpp">
<Filter>Header Files\parser</Filter>
</ClInclude>
<ClInclude Include="..\src\visual_basic_x3.hpp">
<Filter>Header Files\parser</Filter>
</ClInclude>
<ClInclude Include="..\src\vb6_ast.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\src\vb6_ast_adapt.hpp">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,168 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{b5cdf59f-23d6-4e11-a5ae-f1fedd1c8f58}</ProjectGuid>
<RootNamespace>vb6parsertest</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\..\..\miscellaneous.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\..\..\miscellaneous.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\..\..\miscellaneous.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\..\..\miscellaneous.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS;BOOST_MPL_LIMIT_LIST_SIZE=30;GTEST_LINKED_AS_SHARED_LIBRARY;WIN32;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS;BOOST_MPL_LIMIT_LIST_SIZE=30;GTEST_LINKED_AS_SHARED_LIBRARY;WIN32;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS;BOOST_MPL_LIMIT_LIST_SIZE=30;GTEST_LINKED_AS_SHARED_LIBRARY;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS;BOOST_MPL_LIMIT_LIST_SIZE=30;GTEST_LINKED_AS_SHARED_LIBRARY;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\src\vb6_parser_statements_test.cpp" />
<ClCompile Include="..\src\vb6_parser_test.cpp" />
<ClCompile Include="..\src\vb6_parser_test_main.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\src\color_console.hpp" />
<ClInclude Include="..\src\test_grammar_helper.hpp" />
<ClInclude Include="..\src\vb6_ast.hpp" />
<ClInclude Include="..\src\vb6_ast_adapt.hpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="vb6_parser_lib.vcxproj">
<Project>{6808f98c-3361-447b-8f34-f78efb332316}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\src\vb6_parser_test_main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\vb6_parser_statements_test.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\src\vb6_parser_test.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\src\vb6_ast_adapt.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\src\color_console.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\src\test_grammar_helper.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\src\vb6_ast.hpp">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

91
src/color_console.cpp Normal file
View File

@@ -0,0 +1,91 @@
//: color_console.cpp
// vb6_parser
// Copyright (c) 2022 Federico Aponte
// This code is licensed under GNU Software License (see LICENSE.txt for details)
#include "color_console.hpp"
#ifdef _MSC_VER
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#include <iomanip>
#include <iostream>
/*
bit 0 - foreground blue
bit 1 - foreground green
bit 2 - foreground red
bit 3 - foreground intensity
bit 4 - background blue
bit 5 - background green
bit 6 - background red
bit 7 - background intensity
*/
color_manip setcolor(uint8_t col)
{
return color_manip(col);
}
std::ostream& operator<<(std::ostream& os, color_manip c)
{
#ifdef _MSC_VER
HANDLE hstdout = GetStdHandle(STD_OUTPUT_HANDLE);
//SetConsoleMode(STD_OUTPUT_HANDLE, ENABLE_VIRTUAL_TERMINAL_INPUT);
// restore console state
//SetConsoleTextAttribute(hstdout, csbi.wAttributes);
// save console state
//CONSOLE_SCREEN_BUFFER_INFO csbi;
//GetConsoleScreenBufferInfo(hstdout, &csbi);
SetConsoleTextAttribute(hstdout, c.color);
#else
// ANSI escape codes could also be used in some terminals
switch(c.color)
{
case 0: os << std::setfill('c') << "\x1b[30;1m"; break; // bold;black
case 1: os << "\x1b[34;1m"; break; // bold;blue
case 2: os << "\x1b[32;1m"; break; // bold;green
case 3: os << "\x1b[36;1m"; break; // bold;cyan
case 4: os << "\x1b[31;1m"; break; // bold;red
case 5: os << "\x1b[35;1m"; break; // bold;magenta
case 14: os << "\x1b[33;1m"; break; // bold;yellow
case 15: os << "\x1b[37;1m"; break; // bold;white
case 7: os << "\x1b[0m"; break; // reset
}
#endif
return os;
}
std::ostream& tag_ok(std::ostream& os)
{
os << "[" << setcolor(0x02) << "ok" << setcolor(0x07) << "]";
return os;
}
std::ostream& tag_fail(std::ostream& os)
{
os << "[" << setcolor(0x04) << "fail" << setcolor(0x07) << "]";
return os;
}
void wait_key_pressed()
{
#ifdef _MSC_VER
HANDLE hstdin = GetStdHandle(STD_INPUT_HANDLE);
// wait for a key to be pressed
WaitForSingleObject(hstdin, INFINITE);
// what's this for? removes all characters input by the users?
FlushConsoleInputBuffer(hstdin);
#endif
}

25
src/color_console.hpp Normal file
View File

@@ -0,0 +1,25 @@
//: color_console.hpp
// vb6_parser
// Copyright (c) 2022 Federico Aponte
// This code is licensed under GNU Software License (see LICENSE.txt for details)
#pragma once
#include <cstdint>
#include <iostream>
struct color_manip
{
uint8_t color;
explicit color_manip(uint8_t c) : color(c) {}
};
color_manip setcolor(uint8_t col);
std::ostream& operator<<(std::ostream&, color_manip c);
std::ostream& tag_ok(std::ostream&);
std::ostream& tag_fail(std::ostream&);
void wait_key_pressed();

754
src/cpp_ast_printer.cpp Normal file
View File

@@ -0,0 +1,754 @@
//: cpp_ast_printer.cpp
// vb6_parser
// Copyright (c) 2022 Federico Aponte
// This code is licensed under GNU Software License (see LICENSE.txt for details)
#include "cpp_ast_printer.hpp"
#include <boost/optional/optional_io.hpp>
#include <boost/variant/apply_visitor.hpp>
using namespace std;
// static
int cpp_ast_printer::indent_size = 4;
void cpp_ast_printer::operator()(vb6_ast::empty_line const&) const
{
os << '\n';
}
void cpp_ast_printer::operator()(vb6_ast::lonely_comment const& ast) const
{
os << string(indent, ' ');
os << "//" << ast.content << '\n';
}
void cpp_ast_printer::operator()(vb6_ast::identifier_context const& ast) const
{
if(ast.leading_dot)
os << '.';
struct {
void operator()(std::string s) { os << s; }
void operator()(vb6_ast::func_call const& s) { (*p)(s); }
ostream& os;
cpp_ast_printer const* p;
} visitor{ os, this };
for(auto& el : ast.elements)
{
boost::apply_visitor(visitor, el.get());
os << '.';
}
}
void cpp_ast_printer::operator()(vb6_ast::variable const& ast) const
{
//if(ast.library_or_module)
// os << *ast.library_or_module << "::";
os << (ast.type ? *ast.type : "std::any") << " " << ast.name << ";";
}
void cpp_ast_printer::operator()(vb6_ast::decorated_variable const& ast) const
{
(*this)(ast.ctx);
os << ast.var;
}
void cpp_ast_printer::operator()(vb6_ast::func_call const& ast) const
{
os << ast.func_name << "(";
bool first = true;
for(auto& el : ast.params)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ");\n";
}
void cpp_ast_printer::operator()(vb6_ast::global_var_decls const& ast) const
{
print_type(ast.at);
bool first = true;
for(auto& el : ast.vars)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << '\n';
}
void cpp_ast_printer::operator()(vb6_ast::const_expr const& ast) const
{
struct {
void operator()(float v) { os << v << 'f'; }
void operator()(double v) { os << v; }
void operator()(vb6_ast::long_dec v) { os << v.val; }
void operator()(vb6_ast::long_hex v) { os << showbase << hex << v.val << dec; }
void operator()(vb6_ast::long_oct v) { os << showbase << oct << v.val << dec; }
void operator()(vb6_ast::integer_dec v) { os << v.val; }
void operator()(vb6_ast::integer_hex v) { os << showbase << hex << v.val << dec; }
void operator()(vb6_ast::integer_oct v) { os << showbase << oct << v.val << dec; }
void operator()(bool v) { os << (v ? "true" : "false"); }
void operator()(vb6_ast::quoted_string const& s) { os << "\"" << s << "\""; }
void operator()(vb6_ast::nothing const&) { os << "nullptr"; }
ostream& os;
} visitor{os};
boost::apply_visitor(visitor, ast.get());
}
void cpp_ast_printer::operator()(vb6_ast::expression const& ast) const
{
boost::apply_visitor(*this, ast.get());
}
void cpp_ast_printer::operator()(vb6_ast::const_var_stat const& cvars) const
{
os << "Const ";
bool first = true;
for(auto& el : cvars)
{
if(first)
first = false;
else
os << ", ";
os << (el.var.type ? *el.var.type : "std::any")
<< el.var.name << " = ";
(*this)(el.value);
}
}
void cpp_ast_printer::print_type(vb6_ast::access_type t) const
{
switch(t)
{
case vb6_ast::access_type::na: break;
case vb6_ast::access_type::dim: os << "Dim "; break;
case vb6_ast::access_type::private_: os << "Private "; break;
case vb6_ast::access_type::public_: os << "Public "; break;
case vb6_ast::access_type::global: os << "Global "; break;
case vb6_ast::access_type::friend_: os << "Friend "; break;
}
}
void cpp_ast_printer::operator()(vb6_ast::record const& ast) const
{
print_type(ast.at);
os << "struct " << ast.name << '\n' << "{";
indent += indent_size;
for(auto& el : ast.members)
{
os << string(indent, ' ');
(*this)(el);
os << ";\n";
}
indent -= indent_size;
os << "};\n";
}
void cpp_ast_printer::operator()(vb6_ast::vb_enum const& ast) const
{
print_type(ast.at);
os << "enum " << ast.name << '\n' << "{";
indent += indent_size;
for(auto& el : ast.values)
{
os << string(indent, ' ') << el.first;
if(el.second)
{
os << " = ";
(*this)(*el.second);
}
os << ",\n";
}
indent -= indent_size;
os << "};\n";
}
void cpp_ast_printer::operator()(vb6_ast::func_param const& ast) const
{
os << (ast.var.type ? *ast.var.type : "std::any");
if(!ast.qualifier || *ast.qualifier == vb6_ast::param_qualifier::byref)
os << "&";
os << " " << ast.var.name;
if(ast.defvalue)
{
os << " = ";
(*this)(*ast.defvalue);
}
}
void cpp_ast_printer::operator()(vb6_ast::externalSub const& ast) const
{
print_type(ast.at);
os << "__declspec(dllimport)\n"
"void " << ast.name << "(";
bool first = true;
for(auto& el : ast.params)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ");\n";
}
void cpp_ast_printer::operator()(vb6_ast::subHead const& ast) const
{
print_type(ast.at);
os << "void " << ast.name << "(";
bool first = true;
for(auto& el : ast.params)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ");\n";
}
void cpp_ast_printer::operator()(vb6_ast::eventHead const& ast) const
{
print_type(ast.at);
os << "void /* event */ " << ast.name << "(";
bool first = true;
for(auto& el : ast.params)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ");\n";
}
void cpp_ast_printer::operator()(vb6_ast::functionHead const& ast) const
{
print_type(ast.at);
os << ast.return_type << " " << ast.name << "(";
bool first = true;
for(auto& el : ast.params)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ")\n";
}
void cpp_ast_printer::operator()(vb6_ast::propertyLetHead const& ast) const
{
print_type(ast.at);
os << "void let_" << ast.name << "(";
bool first = true;
for(auto& el : ast.params)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ")\n";
}
void cpp_ast_printer::operator()(vb6_ast::propertySetHead const& ast) const
{
print_type(ast.at);
os << "void set_" << ast.name << "(";
bool first = true;
for(auto& el : ast.params)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ")\n";
}
void cpp_ast_printer::operator()(vb6_ast::propertyGetHead const& ast) const
{
print_type(ast.at);
os << ast.return_type << " get_" << ast.name << "(";
bool first = true;
for(auto& el : ast.params)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ")";
if(ast.return_type)
os << " As " << *ast.return_type;
os << '\n';
}
void cpp_ast_printer::operator()(vb6_ast::subDef const& ast) const
{
(*this)(ast.header);
print_block(ast.statements);
}
void cpp_ast_printer::operator()(vb6_ast::functionDef const& ast) const
{
(*this)(ast.header);
print_block(ast.statements);
}
void cpp_ast_printer::operator()(vb6_ast::externalFunction const& ast) const
{
print_type(ast.at);
os << "__declspec(dllimport)\n"
<< *ast.return_type << " " << ast.name << "(";
bool first = true;
for(auto& el : ast.params)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ");\n";
}
void cpp_ast_printer::operator()(vb6_ast::declaration const& ast) const
{
boost::apply_visitor(*this, ast);
}
void cpp_ast_printer::operator()(vb6_ast::module_attribute) const
{
}
void cpp_ast_printer::operator()(vb6_ast::module_option) const
{
}
//void cpp_ast_printer::operator()(vb6_ast::STRICT_MODULE_STRUCTURE::module_attributes const&) const
//{
//}
void cpp_ast_printer::operator()(vb6_ast::STRICT_MODULE_STRUCTURE::vb_module const& ast) const
{
for(auto& el : ast.declarations)
{
boost::apply_visitor(*this, el);
}
for(auto& el : ast.functions)
{
boost::apply_visitor(*this, el);
}
}
void cpp_ast_printer::operator()(vb6_ast::vb_module const& ast) const
{
for(auto& el : ast)
{
boost::apply_visitor(*this, el);
}
}
void cpp_ast_printer::print_block(vb6_ast::statements::statement_block const& block) const
{
os << string(indent, ' ') << "{\n";
indent += indent_size;
for(auto& st : block)
(*this)(st);
indent -= indent_size;
os << string(indent, ' ') << "}\n";
}
void cpp_ast_printer::operator()(vb6_ast::statements::assignStmt const& ast) const
{
os << string(indent, ' ');
switch(ast.type)
{
case vb6_ast::assignmentType::na: break;
case vb6_ast::assignmentType::set: os << "Set "; break;
case vb6_ast::assignmentType::let: os << "Let "; break;
}
(*this)(ast.var);
os << " = ";
(*this)(ast.rhs);
// print the index of the type in a comment
os << " // " << ast.rhs.get().which() << '\n';
}
void cpp_ast_printer::operator()(vb6_ast::statements::exitStmt const& ast) const
{
os << string(indent, ' ');
switch(ast.type)
{
case vb6_ast::exit_type::function: os << "return ???;"; break;
case vb6_ast::exit_type::sub: os << "return;"; break;
case vb6_ast::exit_type::property: os << "Property"; break;
case vb6_ast::exit_type::while_: os << "break;"; break;
case vb6_ast::exit_type::do_: os << "break;"; break;
case vb6_ast::exit_type::for_: os << "break;"; break;
}
os << '\n';
}
void cpp_ast_printer::operator()(vb6_ast::statements::gotoStmt const& ast) const
{
os << string(indent, ' ') << (ast.type == vb6_ast::gotoType::goto_v ? "GoTo" : "GoSub")
<< " " << ast.label << '\n';
}
void cpp_ast_printer::operator()(vb6_ast::statements::onerrorStmt const& ast) const
{
os << string(indent, ' ') << "On Error ";
switch(ast.type)
{
case vb6_ast::onerror_type::goto_0: os << "GoTo 0"; break;
case vb6_ast::onerror_type::goto_neg_1: os << "GoTo -1"; break;
case vb6_ast::onerror_type::goto_label: os << "goto " << ast.label; break;
case vb6_ast::onerror_type::resume_next: os << "Resume Next"; break;
case vb6_ast::onerror_type::exit_sub: os << "Exit Sub"; break;
case vb6_ast::onerror_type::exit_func: os << "Exit Func"; break;
case vb6_ast::onerror_type::exit_property: os << "Exit Property"; break;
}
os << '\n';
}
void cpp_ast_printer::operator()(vb6_ast::statements::resumeStmt const& ast) const
{
os << string(indent, ' ') << "Resume";
switch(ast.type)
{
case vb6_ast::resume_type::implicit:
break;
case vb6_ast::resume_type::next:
os << " Next";
break;
case vb6_ast::resume_type::label:
os << " " << boost::get<string>(ast.label_or_line_nr);
break;
case vb6_ast::resume_type::line_nr:
os << " " << boost::get<int>(ast.label_or_line_nr);
break;
}
os << '\n';
}
void cpp_ast_printer::operator()(vb6_ast::statements::localVarDeclStmt const& ast) const
{
os << string(indent, ' ');
if(ast.type == vb6_ast::localvardeclType::Static)
os << "static ";
bool first = true;
for(auto& el : ast.vars)
{
if(first)
first = false;
else
os << ", ";
os << (el.type ? *el.type : "std::any") << " " << el.name;
}
os << ";\n";
}
void cpp_ast_printer::operator()(vb6_ast::statements::redimStmt const& ast) const
{
os << string(indent, ' ');
if(ast.preserve)
{
// TODO
}
(*this)(ast.var);
os << " = new " << "Type" << "[";
bool first = true;
for(auto& el : ast.newsize)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << "];\n";
}
// vb6_ast::returnStmt
void cpp_ast_printer::operator()(vb6_ast::statements::callStmt const& ast) const
{
os << string(indent, ' ') << ast.sub_name << "(";
bool first = true;
for(auto& el : ast.params)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ");\n";
}
void cpp_ast_printer::operator()(vb6_ast::statements::raiseeventStmt const& ast) const
{
os << string(indent, ' ') << ast.event_name << "(";
bool first = true;
for(auto& el : ast.params)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ");\n";
}
void cpp_ast_printer::operator()(vb6_ast::statements::labelStmt const& ast) const
{
os << ast.label << ":\n"; // no indentation for labels
}
void cpp_ast_printer::operator()(vb6_ast::statements::whileStmt const& ast) const
{
os << string(indent, ' ') << "while(";
(*this)(ast.condition);
os << ")\n";
os << string(indent, ' ') << "{\n";
os << string(indent, ' ') << "}\n";
os << string(indent, ' ') << "while(";
(*this)(ast.condition);
os << ")\n";
print_block(ast.block);
}
void cpp_ast_printer::operator()(vb6_ast::statements::doStmt const& ast) const
{
os << string(indent, ' ') << "for(;;)" << '\n';
print_block(ast.block);
}
void cpp_ast_printer::operator()(vb6_ast::statements::dowhileStmt const& ast) const
{
os << string(indent, ' ') << "while(";
(*this)(ast.condition);
os << ")\n";
print_block(ast.block);
}
void cpp_ast_printer::operator()(vb6_ast::statements::loopwhileStmt const& ast) const
{
os << string(indent, ' ') << "do\n";
os << string(indent, ' ') << "{\n";
indent += indent_size;
for(auto& st : ast.block)
(*this)(st);
indent -= indent_size;
os << string(indent, ' ') << "} while(";
(*this)(ast.condition);
os << ")\n";
}
void cpp_ast_printer::operator()(vb6_ast::statements::dountilStmt const& ast) const
{
os << string(indent, ' ') << "while(not (";
(*this)(ast.condition);
os << "))\n";
print_block(ast.block);
}
void cpp_ast_printer::operator()(vb6_ast::statements::loopuntilStmt const& ast) const
{
os << string(indent, ' ') << "do\n";
os << string(indent, ' ') << "{\n";
indent += indent_size;
for(auto& st : ast.block)
(*this)(st);
indent -= indent_size;
os << string(indent, ' ') << "} while(not (";
(*this)(ast.condition);
os << "))\n";
}
void cpp_ast_printer::operator()(vb6_ast::statements::forStmt const& ast) const
{
os << string(indent, ' ') << "for(";
(*this)(ast.for_variable);
os << " = ";
(*this)(ast.from);
os << "; ";
(*this)(ast.for_variable);
os << " <= ";
(*this)(ast.to);
if(ast.step)
{
os << "; ";
(*this)(ast.for_variable);
os << " += ";
(*this)(*ast.step);
}
else
{
os << "; ++";
(*this)(ast.for_variable);
}
os << ")\n";
print_block(ast.block);
}
void cpp_ast_printer::operator()(vb6_ast::statements::foreachStmt const& ast) const
{
os << string(indent, ' ') << "for(";
(*this)(ast.for_variable);
os << " : ";
(*this)(ast.container);
os << ")\n";
print_block(ast.block);
}
void cpp_ast_printer::operator()(vb6_ast::statements::ifelseStmt const& ast) const
{
// TODO
#ifdef SIMPLE_IF_STATEMENT
os << string(indent, ' ') << "if(";
//(*this)(ast.first_branch);
os << ")\n";
print_block(ast.first_branch.block);
#else
bool first = true;
for(auto& el : ast.if_branches)
{
if(first)
{
os << string(indent, ' ') << "if(";
(*this)(el.condition);
os << ")\n";
}
else
{
os << string(indent, ' ') << "else if(";
(*this)(el.condition);
os << ")\n";
}
print_block(el.block);
}
#endif
if(ast.else_branch.has_value())
{
os << "else\n";
print_block(ast.else_branch.get());
}
}
void cpp_ast_printer::operator()(vb6_ast::statements::withStmt const& ast) const
{
// TODO
os << string(indent, ' ') << "auto& dummy = ";
(*this)(ast.with_variable);
os << ";\n";
// statements
indent += indent_size;
for(auto& st : ast.block)
(*this)(st);
indent -= indent_size;
}
void cpp_ast_printer::operator()(vb6_ast::statements::case_block const& ast) const
{
// TODO
os << string(indent, ' ') << "case ";
(*this)(ast.case_expr);
os << ":\n";
indent += indent_size;
for(auto& st : ast.block)
(*this)(st);
indent -= indent_size;
os << "break;\n";
}
void cpp_ast_printer::operator()(vb6_ast::statements::selectStmt const& ast) const
{
// TODO
os << string(indent, ' ') << "switch(";
(*this)(ast.condition);
os << ")\n";
os << string(indent, ' ') << "{\n";
indent += indent_size;
for(auto& st : ast.blocks)
(*this)(st);
indent -= indent_size;
os << string(indent, ' ') << "}\n";
}
void cpp_ast_printer::operator()(vb6_ast::statements::singleStmt const& ast) const
{
boost::apply_visitor(*this, ast.get());
}

82
src/cpp_ast_printer.hpp Normal file
View File

@@ -0,0 +1,82 @@
//: cpp_ast_printer.hpp
// vb6_parser
// Copyright (c) 2022 Federico Aponte
// This code is licensed under GNU Software License (see LICENSE.txt for details)
#pragma once
#include "vb6_ast.hpp"
#include <iostream>
class cpp_ast_printer
{
public:
explicit cpp_ast_printer(std::ostream& os) : os(os)
{
}
void operator()(vb6_ast::empty_line const&) const;
void operator()(vb6_ast::lonely_comment const&) const;
void operator()(vb6_ast::identifier_context const&) const;
void operator()(vb6_ast::variable const&) const;
void operator()(vb6_ast::decorated_variable const&) const;
void operator()(vb6_ast::const_expr const&) const;
void operator()(vb6_ast::expression const&) const;
void operator()(vb6_ast::func_call const&) const;
void operator()(vb6_ast::global_var_decls const&) const;
void operator()(vb6_ast::const_var_stat const&) const;
void operator()(vb6_ast::record const&) const;
void operator()(vb6_ast::vb_enum const&) const;
void operator()(vb6_ast::func_param const&) const;
void operator()(vb6_ast::externalSub const&) const;
void operator()(vb6_ast::externalFunction const&) const;
void operator()(vb6_ast::subHead const&) const;
void operator()(vb6_ast::eventHead const&) const;
void operator()(vb6_ast::functionHead const&) const;
void operator()(vb6_ast::propertyLetHead const&) const;
void operator()(vb6_ast::propertySetHead const&) const;
void operator()(vb6_ast::propertyGetHead const&) const;
void operator()(vb6_ast::subDef const&) const;
void operator()(vb6_ast::functionDef const&) const;
void operator()(vb6_ast::module_attribute) const;
void operator()(vb6_ast::module_option opt) const;
//void operator()(vb6_ast::STRICT_MODULE_STRUCTURE::module_attributes const&) const;
void operator()(vb6_ast::STRICT_MODULE_STRUCTURE::declaration const&) const;
void operator()(vb6_ast::STRICT_MODULE_STRUCTURE::vb_module const&) const;
void operator()(vb6_ast::declaration const&) const;
void operator()(vb6_ast::vb_module const&) const;
void operator()(vb6_ast::statements::assignStmt const&) const;
void operator()(vb6_ast::statements::exitStmt const&) const;
void operator()(vb6_ast::statements::gotoStmt const&) const;
void operator()(vb6_ast::statements::onerrorStmt const&) const;
void operator()(vb6_ast::statements::resumeStmt const&) const;
void operator()(vb6_ast::statements::localVarDeclStmt const&) const;
void operator()(vb6_ast::statements::redimStmt const&) const;
void operator()(vb6_ast::statements::callStmt const&) const;
void operator()(vb6_ast::statements::raiseeventStmt const&) const;
void operator()(vb6_ast::statements::labelStmt const&) const;
void operator()(vb6_ast::statements::whileStmt const&) const;
void operator()(vb6_ast::statements::doStmt const&) const;
void operator()(vb6_ast::statements::dowhileStmt const&) const;
void operator()(vb6_ast::statements::loopwhileStmt const&) const;
void operator()(vb6_ast::statements::dountilStmt const&) const;
void operator()(vb6_ast::statements::loopuntilStmt const&) const;
void operator()(vb6_ast::statements::forStmt const&) const;
void operator()(vb6_ast::statements::foreachStmt const&) const;
void operator()(vb6_ast::statements::ifelseStmt const&) const;
void operator()(vb6_ast::statements::withStmt const&) const;
void operator()(vb6_ast::statements::case_block const&) const;
void operator()(vb6_ast::statements::selectStmt const&) const;
void operator()(vb6_ast::statements::singleStmt const&) const;
private:
void print_type(vb6_ast::access_type) const;
void print_block(vb6_ast::statements::statement_block const&) const;
std::ostream& os;
mutable int indent = 0;
static int indent_size;
};

983
src/raw_ast_printer.cpp Normal file
View File

@@ -0,0 +1,983 @@
//: raw_ast_printer.cpp
// vb6_parser
// Copyright (c) 2022 Federico Aponte
// This code is licensed under GNU Software License (see LICENSE.txt for details)
#include "raw_ast_printer.hpp"
#include <boost/variant/apply_visitor.hpp>
using namespace std;
// static
int raw_ast_printer::indent_size = 4;
void raw_ast_printer::operator()(vb6_ast::empty_line const& ast) const
{
// (empty_line)
os << string(indent, ' ') << '(' << typeid(ast).name() << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::lonely_comment const& ast) const
{
// (lonely_comment "this is a comment")
os << string(indent, ' ') << '(' << typeid(ast).name() << " \"" << ast.content << "\")\n";
}
void raw_ast_printer::operator()(vb6_ast::identifier_context const& ast) const
{
// (identifier_context no_dot (func_call foo) depth)
os << string(indent, ' ') << '(' << typeid(ast).name();
os << (ast.leading_dot ? "dot" : "no_dot");
struct {
void operator()(string s) { os << s; }
void operator()(vb6_ast::func_call const& s) { (*p)(s); }
ostream& os;
raw_ast_printer const* p;
} visitor{ os, this };
for(auto& el : ast.elements)
{
boost::apply_visitor(visitor, el.get());
os << " ";
}
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::variable const& ast) const
{
// (variable x Integer no_new)
os << string(indent, ' ') << '(' << typeid(ast).name()
<< " " << ast.name;
if(ast.type)
{
os << " " << *ast.type;
if(ast.construct)
os << " new";
//if(ast.library_or_module)
// os << *ast.library_or_module << '.';
}
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::decorated_variable const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name()
<< " " << ast.var;
(*this)(ast.ctx);
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::func_call const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name()
<< " " << ast.func_name;
bool first = true;
for(auto& el : ast.params)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::global_var_decls const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
print_type(ast.at);
bool first = true;
for(auto& el : ast.vars)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::const_expr const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
struct {
void operator()(float v) { os << v << '!'; }
void operator()(double v) { os << v << '#'; }
void operator()(vb6_ast::long_dec v) { os << v.val << '&'; }
void operator()(vb6_ast::long_hex v) { os << "&H" << noshowbase << hex << v.val << dec << '&'; }
void operator()(vb6_ast::long_oct v) { os << "&0" << noshowbase << oct << v.val << dec << '&'; }
void operator()(vb6_ast::integer_dec v) { os << v.val << '%'; }
void operator()(vb6_ast::integer_hex v) { os << "&H" << noshowbase << hex << v.val << dec << '%'; }
void operator()(vb6_ast::integer_oct v) { os << "&0" << noshowbase << hex << v.val << dec << '%'; }
void operator()(bool v) { os << (v ? "True" : "False"); }
void operator()(vb6_ast::quoted_string const& s) { os << "\"" << s << "\""; }
void operator()(vb6_ast::nothing const&) { os << "Nothing"; }
ostream& os;
} visitor{os};
boost::apply_visitor(visitor, ast.get());
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::expression const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
boost::apply_visitor(*this, ast.get());
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::const_var_stat const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
bool first = true;
for(auto& el : ast)
{
if(first)
first = false;
else
os << ", ";
os << el.var.name;
//os << " As " << (el.var.type ? *el.var.type : "<unspecified>");
if(el.var.type)
os << " As " << *el.var.type;
os << " = ";
(*this)(el.value);
}
os << ")\n";
}
void raw_ast_printer::print_type(vb6_ast::access_type t) const
{
switch(t)
{
case vb6_ast::access_type::na: break;
case vb6_ast::access_type::dim: os << "Dim "; break;
case vb6_ast::access_type::private_: os << "Private "; break;
case vb6_ast::access_type::public_: os << "Public "; break;
case vb6_ast::access_type::global: os << "Global "; break;
case vb6_ast::access_type::friend_: os << "Friend "; break;
}
}
void raw_ast_printer::operator()(vb6_ast::record const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
print_type(ast.at);
os << "Type " << ast.name << '\n';
indent += indent_size;
for(auto& el : ast.members)
{
os << string(indent, ' ');
(*this)(el);
os << '\n';
}
indent -= indent_size;
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::vb_enum const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
print_type(ast.at);
os << ast.name << '\n';
indent += indent_size;
for(auto& el : ast.values)
{
os << string(indent, ' ') << el.first;
if(el.second)
{
os << " = ";
(*this)(*el.second);
}
os << '\n';
}
indent -= indent_size;
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::func_param const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
if(ast.isoptional)
os << "Optional ";
if(ast.qualifier)
os << (*ast.qualifier == vb6_ast::param_qualifier::byref ? "ByRef" : "ByVal") << " ";
os << ast.var.name;
//os << " As " << (ast.var.type ? *ast.var.type : "<unspecified>");
if(ast.var.type)
os << " As " << *ast.var.type;
if(ast.defvalue)
{
os << " = ";
(*this)(*ast.defvalue);
}
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::externalSub const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
print_type(ast.at);
os << ast.name << " Lib \"" << ast.lib << "\" Alias \"" << ast.alias << "\" (";
bool first = true;
for(auto& el : ast.params)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::subHead const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
print_type(ast.at);
os << "Sub " << ast.name << "(";
bool first = true;
for(auto& el : ast.params)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::eventHead const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
print_type(ast.at);
os << ast.name << "(";
bool first = true;
for(auto& el : ast.params)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::functionHead const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
print_type(ast.at);
os << "Function " << ast.name << "(";
bool first = true;
for(auto& el : ast.params)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ")";
if(ast.return_type)
os << " As " << *ast.return_type;
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::propertyLetHead const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
print_type(ast.at);
os << ast.name << "(";
bool first = true;
for(auto& el : ast.params)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::propertySetHead const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
print_type(ast.at);
os << ast.name << "(";
bool first = true;
for(auto& el : ast.params)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::propertyGetHead const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
print_type(ast.at);
os << ast.name << "(";
bool first = true;
for(auto& el : ast.params)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ")";
if(ast.return_type)
os << " As " << *ast.return_type;
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::subDef const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
(*this)(ast.header);
indent += indent_size;
for(auto& el : ast.statements)
{
(*this)(el);
}
indent -= indent_size;
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::functionDef const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
(*this)(ast.header);
indent += indent_size;
for(auto& el : ast.statements)
{
(*this)(el);
}
indent -= indent_size;
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::externalFunction const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
print_type(ast.at);
os << "Declare Function " << ast.name << " Lib \"" << ast.lib << "\" Alias \"" << ast.alias << "\" (";
bool first = true;
for(auto& el : ast.params)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ")";
if(ast.return_type)
os << " As " << *ast.return_type;
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::module_attribute ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
os << " " << ast.first << " " << ast.second;
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::module_option ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
switch(ast)
{
case vb6_ast::module_option::explicit_:
os << "Option Explicit";
break;
case vb6_ast::module_option::base_0:
os << "Option Base 0";
break;
case vb6_ast::module_option::base_1:
os << "Option Base 1";
break;
case vb6_ast::module_option::compare_binary:
os << "Option Compare Binary";
break;
case vb6_ast::module_option::compare_text:
os << "Option Compare Text";
break;
case vb6_ast::module_option::private_module:
os << "Option Private Module";
break;
}
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::STRICT_MODULE_STRUCTURE::module_attributes const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
for(auto& el : ast)
{
os << el.first << " " << el.second << "\"\n";
}
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::STRICT_MODULE_STRUCTURE::declaration const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
boost::apply_visitor(*this, ast);
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::STRICT_MODULE_STRUCTURE::vb_module const& ast) const
{
(*this)(ast.attrs);
os << "'---------------------------------------- options\n";
for(auto& el : ast.opts.items)
{
#if 0
(*this)(el);
#else
boost::apply_visitor(*this, el);
#endif
}
os << "'---------------------------------------- declarations\n";
for(auto& el : ast.declarations)
{
(*this)(el);
}
os << "'---------------------------------------- functions\n";
for(auto& el : ast.functions)
{
boost::apply_visitor(*this, el);
}
}
void raw_ast_printer::operator()(vb6_ast::declaration const& ast) const
{
boost::apply_visitor(*this, ast);
}
void raw_ast_printer::operator()(vb6_ast::vb_module const& ast) const
{
for(auto& el : ast)
{
boost::apply_visitor(*this, el);
}
}
void raw_ast_printer::operator()(vb6_ast::statements::assignStmt const& ast) const
{
os << string(indent, ' ');
switch(ast.type)
{
case vb6_ast::assignmentType::na: break;
case vb6_ast::assignmentType::set: os << "Set "; break;
case vb6_ast::assignmentType::let: os << "Let "; break;
}
(*this)(ast.var);
os << " = ";
(*this)(ast.rhs);
// print the index of the type in a comment
os << " ' " << ast.rhs.get().which() << '\n';
}
void raw_ast_printer::operator()(vb6_ast::statements::exitStmt const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
switch(ast.type)
{
case vb6_ast::exit_type::function: os << "Function"; break;
case vb6_ast::exit_type::sub: os << "Sub"; break;
case vb6_ast::exit_type::property: os << "Property"; break;
case vb6_ast::exit_type::while_: os << "While"; break;
case vb6_ast::exit_type::do_: os << "Do"; break;
case vb6_ast::exit_type::for_: os << "For"; break;
}
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::statements::gotoStmt const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
os << ' ' << (ast.type == vb6_ast::gotoType::goto_v ? "GoTo" : "GoSub")
<< " " << ast.label
<< ")\n";
}
void raw_ast_printer::operator()(vb6_ast::statements::onerrorStmt const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
switch(ast.type)
{
case vb6_ast::onerror_type::goto_0: os << "GoTo 0"; break;
case vb6_ast::onerror_type::goto_neg_1: os << "GoTo -1"; break;
case vb6_ast::onerror_type::goto_label: os << "GoTo " << ast.label; break;
case vb6_ast::onerror_type::resume_next: os << "Resume Next"; break;
case vb6_ast::onerror_type::exit_sub: os << "Exit Sub"; break;
case vb6_ast::onerror_type::exit_func: os << "Exit Func"; break;
case vb6_ast::onerror_type::exit_property: os << "Exit Property"; break;
}
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::statements::resumeStmt const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
switch(ast.type)
{
case vb6_ast::resume_type::implicit:
break;
case vb6_ast::resume_type::next:
os << " Next";
break;
case vb6_ast::resume_type::label:
os << " " << boost::get<string>(ast.label_or_line_nr);
break;
case vb6_ast::resume_type::line_nr:
os << " " << boost::get<int>(ast.label_or_line_nr);
break;
}
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::statements::localVarDeclStmt const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
if(ast.type == vb6_ast::localvardeclType::Static)
os << "Static ";
else
os << "Dim ";
bool first = true;
for(auto& el : ast.vars)
{
if(first)
first = false;
else
os << ", ";
os << el.name;
if(el.type)
{
os << " As ";
if(el.construct)
os << "New ";
os << *el.type;
}
//else
// os << " As <unspecified>";
}
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::statements::redimStmt const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
if(ast.preserve)
os << "preserve ";
(*this)(ast.var);
os << " ";
bool first = true;
for(auto& el : ast.newsize)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ")\n";
}
// vb6_ast::returnStmt
void raw_ast_printer::operator()(vb6_ast::statements::callStmt const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
if(ast.explicit_call)
os << "Call " << ast.sub_name << "(";
else
os << ast.sub_name << " ";
bool first = true;
for(auto& el : ast.params)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::statements::raiseeventStmt const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
os << " " << ast.event_name;
bool first = true;
for(auto& el : ast.params)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::statements::labelStmt const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name()
<< " " << ast.label << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::statements::whileStmt const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
(*this)(ast.condition);
os << '\n';
indent += indent_size;
for(auto& st : ast.block)
(*this)(st);
indent -= indent_size;
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::statements::doStmt const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
indent += indent_size;
for(auto& st : ast.block)
(*this)(st);
indent -= indent_size;
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::statements::dowhileStmt const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
(*this)(ast.condition);
os << '\n';
indent += indent_size;
for(auto& st : ast.block)
(*this)(st);
indent -= indent_size;
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::statements::dountilStmt const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
(*this)(ast.condition);
os << '\n';
indent += indent_size;
for(auto& st : ast.block)
(*this)(st);
indent -= indent_size;
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::statements::loopwhileStmt const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
indent += indent_size;
for(auto& st : ast.block)
(*this)(st);
indent -= indent_size;
os << string(indent, ' ') << "Loop While ";
(*this)(ast.condition);
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::statements::loopuntilStmt const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
indent += indent_size;
for(auto& st : ast.block)
(*this)(st);
indent -= indent_size;
os << string(indent, ' ') << "Loop Until ";
(*this)(ast.condition);
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::statements::forStmt const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
(*this)(ast.for_variable);
os << " = ";
(*this)(ast.from);
os << " To ";
(*this)(ast.to);
if(ast.step)
{
os << " Step ";
(*this)(*ast.step);
}
os << '\n';
indent += indent_size;
for(auto& el : ast.block)
(*this)(el);
indent -= indent_size;
os << string(indent, ' ') << "Next ";
(*this)(ast.for_variable);
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::statements::foreachStmt const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
(*this)(ast.for_variable);
os << " In ";
(*this)(ast.container);
os << '\n';
indent += indent_size;
for(auto& el : ast.block)
(*this)(el);
indent -= indent_size;
os << string(indent, ' ') << "Next ";
(*this)(ast.for_variable);
os << ")\n";
}
void raw_ast_printer::operator()(vb6_ast::statements::ifelseStmt const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
#ifdef SIMPLE_IF_STATEMENT
(*this)(ast.first_branch.condition);
// statements
indent += indent_size;
for(auto& st : ast.first_branch.block)
(*this)(st);
indent -= indent_size;
#else
(*this)(ast.first_branch.condition);
// statements
indent += indent_size;
for(auto& st : ast.first_branch.block)
(*this)(st);
indent -= indent_size;
//bool first = true;
for(auto& el : ast.if_branches)
{
os << string(indent, ' ');
//if(first)
//{
// os << "If ";
// first = false;
//}
//else
os << "ElseIf ";
(*this)(el.condition);
os << " Then\n";
indent += indent_size;
for(auto& st : el.block)
(*this)(st);
indent -= indent_size;
}
#endif
if(ast.else_branch.has_value())
{
os << string(indent, ' ') << "Else " << '\n';
indent += indent_size;
for(auto& el : ast.else_branch.get())
(*this)(el);
indent -= indent_size;
}
os << string(indent, ' ') << "End If\n";
}
void raw_ast_printer::operator()(vb6_ast::statements::withStmt const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
(*this)(ast.with_variable);
os << '\n';
// statements
indent += indent_size;
for(auto& st : ast.block)
(*this)(st);
indent -= indent_size;
os << string(indent, ' ') << "End With\n";
}
void raw_ast_printer::operator()(vb6_ast::statements::case_block const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
// TODO
(*this)(ast.case_expr);
os << "\n";
indent += indent_size;
for(auto& st : ast.block)
(*this)(st);
indent -= indent_size;
}
void raw_ast_printer::operator()(vb6_ast::statements::selectStmt const& ast) const
{
os << string(indent, ' ') << '(' << typeid(ast).name();
// TODO
(*this)(ast.condition);
os << '\n';
indent += indent_size;
for(auto& st : ast.blocks)
(*this)(st);
indent -= indent_size;
os << string(indent, ' ') << "End Case\n";
}
void raw_ast_printer::operator()(vb6_ast::statements::singleStmt const& ast) const
{
//os << string(indent, ' '); // FED ???? wouldn't it be better to indent here rather than at each statement?
// => problem with some statements, e.g. else within the if-statement
// perhaps the else should be a statement of its own!!!
boost::apply_visitor(*this, ast.get());
}

81
src/raw_ast_printer.hpp Normal file
View File

@@ -0,0 +1,81 @@
//: raw_ast_printer.hpp
// vb6_parser
// Copyright (c) 2022 Federico Aponte
// This code is licensed under GNU Software License (see LICENSE.txt for details)
#pragma once
#include "vb6_ast.hpp"
#include <iostream>
class raw_ast_printer
{
public:
explicit raw_ast_printer(std::ostream& os) : os(os)
{
}
void operator()(vb6_ast::empty_line const&) const;
void operator()(vb6_ast::lonely_comment const&) const;
void operator()(vb6_ast::identifier_context const&) const;
void operator()(vb6_ast::variable const&) const;
void operator()(vb6_ast::decorated_variable const&) const;
void operator()(vb6_ast::const_expr const&) const;
void operator()(vb6_ast::expression const&) const;
void operator()(vb6_ast::func_call const&) const;
void operator()(vb6_ast::global_var_decls const&) const;
void operator()(vb6_ast::const_var_stat const&) const;
void operator()(vb6_ast::record const&) const;
void operator()(vb6_ast::vb_enum const&) const;
void operator()(vb6_ast::func_param const&) const;
void operator()(vb6_ast::externalSub const&) const;
void operator()(vb6_ast::externalFunction const&) const;
void operator()(vb6_ast::subHead const&) const;
void operator()(vb6_ast::eventHead const&) const;
void operator()(vb6_ast::functionHead const&) const;
void operator()(vb6_ast::propertyLetHead const&) const;
void operator()(vb6_ast::propertySetHead const&) const;
void operator()(vb6_ast::propertyGetHead const&) const;
void operator()(vb6_ast::subDef const&) const;
void operator()(vb6_ast::functionDef const&) const;
void operator()(vb6_ast::module_attribute) const;
void operator()(vb6_ast::module_option) const;
void operator()(vb6_ast::STRICT_MODULE_STRUCTURE::module_attributes const&) const;
void operator()(vb6_ast::STRICT_MODULE_STRUCTURE::declaration const&) const;
void operator()(vb6_ast::STRICT_MODULE_STRUCTURE::vb_module const&) const;
void operator()(vb6_ast::declaration const&) const;
void operator()(vb6_ast::vb_module const&) const;
void operator()(vb6_ast::statements::assignStmt const&) const;
void operator()(vb6_ast::statements::exitStmt const&) const;
void operator()(vb6_ast::statements::gotoStmt const&) const;
void operator()(vb6_ast::statements::onerrorStmt const&) const;
void operator()(vb6_ast::statements::resumeStmt const&) const;
void operator()(vb6_ast::statements::localVarDeclStmt const&) const;
void operator()(vb6_ast::statements::redimStmt const&) const;
void operator()(vb6_ast::statements::callStmt const&) const;
void operator()(vb6_ast::statements::raiseeventStmt const&) const;
void operator()(vb6_ast::statements::labelStmt const&) const;
void operator()(vb6_ast::statements::whileStmt const&) const;
void operator()(vb6_ast::statements::doStmt const&) const;
void operator()(vb6_ast::statements::dowhileStmt const&) const;
void operator()(vb6_ast::statements::loopwhileStmt const&) const;
void operator()(vb6_ast::statements::dountilStmt const&) const;
void operator()(vb6_ast::statements::loopuntilStmt const&) const;
void operator()(vb6_ast::statements::forStmt const&) const;
void operator()(vb6_ast::statements::foreachStmt const&) const;
void operator()(vb6_ast::statements::ifelseStmt const&) const;
void operator()(vb6_ast::statements::withStmt const&) const;
void operator()(vb6_ast::statements::case_block const&) const;
void operator()(vb6_ast::statements::selectStmt const&) const;
void operator()(vb6_ast::statements::singleStmt const&) const;
private:
void print_type(vb6_ast::access_type) const;
std::ostream& os;
mutable int indent = 0;
static int indent_size;
};

50
src/test_gosub.cpp Normal file
View File

@@ -0,0 +1,50 @@
//
#include <csetjmp>
#include <iostream>
#define VB6_GOSUB(sub) if(!setjmp(_gs)){goto sub;}
#define VB6_RETURN longjmp(_gs,1)
using namespace std;
/*
Sub foo()
GoSub mysub1
Exit Sub
mysub1:
Debug.Print "mysub1"
Return
mysub2:
Debug.Print "mysub2"
Return
End Sub
#define GOSUB(sub) if(!setjmp(_gs)){goto sub;}
#define RETURN longjmp(_gs,1)
*/
void test_gosub(ostream& os)
{
os << "---- begin " << __func__ << " ----\n";
jmp_buf _gs;
VB6_GOSUB(sub1);
os << " after calling subroutine 1\n";
VB6_GOSUB(sub2);
os << " after calling subroutine 2\n";
return;
sub1:
os << " in subroutine 1\n";
VB6_RETURN;
sub2:
os << " in subroutine 2\n";
VB6_RETURN;
}

116
src/test_grammar_helper.hpp Normal file
View File

@@ -0,0 +1,116 @@
//: test_grammar_helper.hpp
// vb6_parser
// Copyright (c) 2022 Federico Aponte
// This code is licensed under GNU Software License (see LICENSE.txt for details)
#pragma once
#include "color_console.hpp"
#include "vb6_config.hpp"
#include <gtest/gtest.h>
#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <string>
#include <string_view>
template <class ruleType, class attrType>
void test_grammar(std::ostream& os, std::string_view testName, ruleType rule,
std::string_view fragment, attrType& attr)
{
namespace x3 = boost::spirit::x3;
os << "--------\n" << testName << "\n--------\n";
auto it1 = cbegin(fragment);
auto const it2 = cend(fragment);
try
{
std::stringstream out;
vb6_grammar::error_handler_type error_handler(it1, it2, out, "source.bas");
auto const parser =
// we pass our error handler to the parser so we can access
// it later on in our on_error and on_success handlers
x3::with<vb6_grammar::vb6_error_handler_tag>(std::ref(error_handler))
[
rule
];
// NOTE:
// see https://www.codevamping.com/2018/12/spirit-x3-file-organization/
// "Be sure to call phrase_parse without a namespace.
// Do not call it like x3::phrase_parse.
// Doing so turns off Argument Dependent Lookup. Evil ensues."
// It appears that using a namespace is not a problem here,
// but the article is interesting.
bool res = x3::phrase_parse(it1, it2, parser, vb6_grammar::skip, attr);
if(res)
{
os << tag_ok << '\n';
if(it1 != it2)
os << " but we stopped at " << std::string(it1, it2) << '\n';
}
else
os << tag_fail << " - stopped at: " << std::string(it1, it2) << '\n';
}
catch(x3::expectation_failure<decltype(it1)>& e)
{
os << tag_fail << " - " << e.what() << " - " << (e.where() - it1) << " - " << e.which() << '\n';
}
}
template <class ruleType, class attrType>
void test_grammar(std::string_view fragment, ruleType rule, attrType& attr)
{
namespace x3 = boost::spirit::x3;
auto it1 = cbegin(fragment);
auto const it2 = cend(fragment);
std::stringstream out;
vb6_grammar::error_handler_type error_handler(it1, it2, out, "source.bas");
auto const parser =
// we pass our error handler to the parser so we can access
// it later on in our on_error and on_success handlers
x3::with<vb6_grammar::vb6_error_handler_tag>(std::ref(error_handler))
[
rule
];
ASSERT_TRUE(x3::phrase_parse(it1, it2, parser, vb6_grammar::skip, attr))
<< "stopped at: " << std::string(it1, it2);
EXPECT_EQ(it1, it2);
}
template <class ruleType, class attrType>
void test_grammar_false(std::string_view fragment, ruleType rule, attrType& attr)
{
namespace x3 = boost::spirit::x3;
auto it1 = cbegin(fragment);
auto const it2 = cend(fragment);
std::stringstream out;
vb6_grammar::error_handler_type error_handler(it1, it2, out, "source.bas");
auto const parser =
// we pass our error handler to the parser so we can access
// it later on in our on_error and on_success handlers
x3::with<vb6_grammar::vb6_error_handler_tag>(std::ref(error_handler))
[
rule
];
ASSERT_FALSE(x3::phrase_parse(it1, it2, parser, vb6_grammar::skip, attr));
}

716
src/vb6_ast.hpp Normal file
View File

@@ -0,0 +1,716 @@
//: vb6_ast.hpp
// vb6_parser
// Copyright (c) 2022 Federico Aponte
// This code is licensed under GNU Software License (see LICENSE.txt for details)
#pragma once
#include <boost/optional/optional.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/spirit/home/x3/support/ast/position_tagged.hpp>
#include <map>
#include <string>
#include <vector>
namespace vb6_ast {
namespace x3 = boost::spirit::x3;
enum class NativeType
{
Unspecified,
Boolean,
Byte,
Integer,
Long,
Single,
Double,
Currency,
Date,
String,
Object,
Variant,
NonNative
};
enum class rel_operator_type
{
less,
greater,
less_equal,
greater_equal,
equal,
not_equal
};
enum class operator_type
{
less,
greater,
less_equal,
greater_equal,
equal,
not_equal,
plus,
minus,
mult,
div,
div_int,
exp,
diff,
assign,
amp,
mod,
imp,
not_, // unary
and_,
xor_,
is,
like
};
enum class access_type
{
na,
dim,
global,
public_,
private_,
friend_
};
enum class param_qualifier
{
byval,
byref
};
enum class assignmentType
{
na = 0,
let = 1,
set = 2
};
enum class localvardeclType
{
Dim = 0,
Static = 1
};
enum class gotoType
{
goto_v = 0,
gosub_v = 1
};
enum class onerror_type
{
goto_0, // disables enabled error handler in the current procedureand resets it to Nothing
goto_neg_1, // disables enabled exception in the current procedure and resets it to Nothing
goto_label,
exit_sub,
exit_func,
exit_property,
resume_next
};
enum class resume_type
{
implicit,
next,
line_nr,
label
};
enum class exit_type
{
sub,
function,
property,
while_,
do_,
for_
};
enum class module_option
{
explicit_,
base_0,
base_1,
compare_text,
compare_binary,
private_module
};
struct func_call;
struct nothing
{
};
struct empty_line
{
};
struct lonely_comment //: std::string
{
std::string content;
};
struct quoted_string : std::string
{
};
using var_identifier = std::string;
struct identifier_context : x3::position_tagged
{
bool leading_dot = false;
std::vector<x3::variant<x3::forward_ast<func_call>, std::string>> elements;
};
//using simple_type_identifier = std::pair<boost::optional<std::string>, std::string>;
//using complex_type_identifier = std::pair<boost::optional<std::string>, std::string>;
using simple_type_identifier = std::string;
using complex_type_identifier = std::string;
// TODO use this as the class to encode the type in a declaration for variables and parameters
struct type_identifier : x3::position_tagged
{
NativeType type = NativeType::NonNative;
boost::optional<std::string> library_or_module;
std::string nonnative_type;
};
// Ex.: frm As VB.Form
// Ex.: frm As New MyLib.MyClass
struct variable : x3::position_tagged
{
std::string name;
bool construct = false; // has the 'New' specifier
//boost::optional<std::string> library_or_module;
//simple_type_identifier type; // not sure...
boost::optional<simple_type_identifier> type; // not sure...
};
struct decorated_variable : x3::position_tagged
{
identifier_context ctx;
var_identifier var;
};
// Ex.: Private g_FinalName As String, g_FinalType As Integer
struct global_var_decls : x3::position_tagged
{
access_type at = access_type::na;
bool with_events = false;
std::vector<variable> vars;
};
struct integer_dec { short val; };
struct integer_hex { short val; };
struct integer_oct { short val; };
struct long_dec { int val; };
struct long_hex { int val; };
struct long_oct { int val; };
using const_expr = x3::variant<float, double,
long_dec, long_hex, long_oct,
integer_dec, integer_hex, integer_oct,
quoted_string, bool, nothing>;
// Ex.: x As Integer = 100
struct const_var : x3::position_tagged
{
variable var;
const_expr value;
};
struct const_var_stat : std::vector<const_var>
{
};
// Ex.: Public Type MyPoint: x As Integer: y As Integer: End Type
struct record : x3::position_tagged
{
access_type at = access_type::na;
std::string name;
std::vector<variable> members;
};
// FED ???? instead of a const_expr consider using an integer (how large?) for the value
using enum_item = std::pair<std::string, boost::optional<const_expr>>;
// Ex.: Public Enum ComprKind: NoCompr = 0: Jpeg = 1: Tiff = 2: End Enum
struct vb_enum : x3::position_tagged
{
access_type at = access_type::na;
std::string name;
std::vector<enum_item> values;
};
struct expression : x3::variant<
const_expr,
decorated_variable,
x3::forward_ast<func_call>>
{
using base_type::base_type;
using base_type::operator=;
};
struct func_call : x3::position_tagged
{
std::string func_name;
std::vector<expression> params;
};
// Ex.: Optional ByVal flag As Boolean = True
struct func_param : x3::position_tagged
{
bool isoptional = false;
boost::optional<param_qualifier> qualifier; // byval, byref
variable var;
boost::optional<const_expr> defvalue;
};
struct external_decl : x3::position_tagged
{
std::string name;
access_type at;
std::vector<func_param> params;
};
// Ex.: Private Sub CalcTotal(ByVal algorithm As Integer)
struct subHead : x3::position_tagged
{
access_type at = access_type::na;
std::string name;
std::vector<func_param> params;
};
// Ex.: Public Event OnShow(ByVal text As String)
struct eventHead : x3::position_tagged
{
access_type at; // private, public, friend
std::string name;
std::vector<func_param> params;
};
// Ex.: Private Function GetMagicWord(ByVal spell As String) As String
struct functionHead : x3::position_tagged
{
access_type at = access_type::na;
std::string name;
std::vector<func_param> params;
boost::optional<simple_type_identifier> return_type; // not sure...
};
// Ex.: Public Property Let Title(str As String)
struct propertyLetHead : x3::position_tagged
{
access_type at = access_type::na;
std::string name;
std::vector<func_param> params;
};
// Ex.: Public Property Set Title(str As String)
struct propertySetHead : x3::position_tagged
{
access_type at = access_type::na;
std::string name;
std::vector<func_param> params;
};
// Ex.: Public Property Get Title() As String
struct propertyGetHead : x3::position_tagged
{
access_type at = access_type::na;
std::string name;
std::vector<func_param> params; // FED ???? does Property Get take parameters? maybe for arrays?
boost::optional<simple_type_identifier> return_type; // not sure...
};
// Ex.: Private Sub Beep Lib "kernel32" Alias "Beep" (ByVal freq As Integer, ByVal duration As Integer)
struct externalSub : x3::position_tagged
{
access_type at = access_type::na;
std::string name;
std::string lib;
std::string alias;
std::vector<func_param> params;
};
// Ex.: Private Function Compress Lib "myzip" Alias "Compress" (ByVal str As String) As Integer
struct externalFunction : x3::position_tagged
{
access_type at = access_type::na;
std::string name;
std::string lib;
std::string alias;
std::vector<func_param> params;
boost::optional<simple_type_identifier> return_type; // not sure...
};
// statements (things that go into functions, subroutines and property definitions)
namespace statements {
struct assignStmt : x3::position_tagged
{
assignmentType type = assignmentType::na;
decorated_variable var;
expression rhs;
};
struct localVarDeclStmt : x3::position_tagged
{
localvardeclType type;
std::vector<variable> vars;
};
struct redimStmt : x3::position_tagged
{
bool preserve = false;
decorated_variable var;
std::vector<expression> newsize;
};
struct exitStmt : x3::position_tagged
{
exit_type type; // Sub, Function, Property, For, Do, While, ...
};
struct gotoStmt : x3::position_tagged
{
gotoType type;
std::string label;
};
struct onerrorStmt : x3::position_tagged
{
onerror_type type;
std::string label;
};
struct resumeStmt : x3::position_tagged
{
resume_type type;
x3::variant<std::string, int> label_or_line_nr;
};
struct labelStmt : x3::position_tagged
{
std::string label;
};
struct callStmt : x3::position_tagged
{
std::string sub_name;
std::vector<expression> params;
bool explicit_call;
};
struct raiseeventStmt : x3::position_tagged
{
std::string event_name;
std::vector<expression> params;
};
// forward declarations for compound statements
struct whileStmt;
struct doStmt;
struct dowhileStmt;
struct loopwhileStmt;
struct dountilStmt;
struct loopuntilStmt;
struct forStmt;
struct foreachStmt;
struct ifelseStmt;
struct withStmt;
struct selectStmt;
// FED ???? pazzesco! senza questi #define non compila
//#define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS
//#define BOOST_MPL_LIMIT_LIST_SIZE 30
struct singleStmt : x3::variant<
lonely_comment,
empty_line,
assignStmt,
localVarDeclStmt,
redimStmt,
exitStmt,
gotoStmt,
onerrorStmt,
resumeStmt,
labelStmt,
callStmt,
raiseeventStmt,
// compound statements
x3::forward_ast<whileStmt>,
x3::forward_ast<doStmt>,
x3::forward_ast<dowhileStmt>,
x3::forward_ast<loopwhileStmt>,
x3::forward_ast<dountilStmt>,
x3::forward_ast<loopuntilStmt>,
x3::forward_ast<forStmt>,
x3::forward_ast<foreachStmt>,
x3::forward_ast<ifelseStmt>,
x3::forward_ast<selectStmt>,
x3::forward_ast<withStmt>
>
{
using base_type::base_type;
using base_type::operator=;
};
using statement_block = std::vector<singleStmt>;
// compound statements
struct whileStmt : x3::position_tagged
{
expression condition;
statement_block block;
};
struct doStmt : x3::position_tagged
{
statement_block block;
};
struct dowhileStmt : x3::position_tagged
{
expression condition;
statement_block block;
};
struct loopwhileStmt : x3::position_tagged
{
statement_block block;
expression condition;
};
struct dountilStmt : x3::position_tagged
{
expression condition;
statement_block block;
};
struct loopuntilStmt : x3::position_tagged
{
statement_block block;
expression condition;
};
struct forStmt : x3::position_tagged
{
decorated_variable for_variable;
expression from;
expression to;
boost::optional<expression> step;
statement_block block;
};
// TODO
struct foreachStmt : x3::position_tagged
{
decorated_variable for_variable;
expression container;
statement_block block;
};
struct if_branch : x3::position_tagged
{
expression condition;
statement_block block;
};
// TODO
struct ifelseStmt : x3::position_tagged
{
//expression condition;
//statement_block block;
#ifdef SIMPLE_IF_STATEMENT
if_branch first_branch;
#else
if_branch first_branch;
std::vector<if_branch> if_branches;
#endif
boost::optional<statement_block> else_branch;
};
struct withStmt : x3::position_tagged
{
decorated_variable with_variable;
statement_block block;
};
// TODO
struct case_relational_expr : x3::position_tagged
{
rel_operator_type rel_op;
expression rexpr;
};
// TODO
struct case_block : x3::position_tagged
{
expression case_expr;
//std::vector<x3::variant<case_relational_expr, std::pair<expression, expression>>> case_expr_list;
statement_block block;
};
struct selectStmt : x3::position_tagged
{
expression condition;
std::vector<case_block> blocks;
};
} // namespace statements
struct subDef : x3::position_tagged
{
subHead header;
statements::statement_block statements;
};
struct functionDef : x3::position_tagged
{
functionHead header;
statements::statement_block statements;
};
struct get_prop : x3::position_tagged
{
std::string name;
access_type at;
std::vector<func_param> params;
statements::statement_block statements;
};
struct let_prop : x3::position_tagged
{
std::string name;
access_type at;
std::vector<func_param> params;
statements::statement_block stats;
};
struct set_prop : x3::position_tagged
{
std::string name;
access_type at;
statements::statement_block statements;
};
namespace STRICT_MODULE_STRUCTURE
{
struct module_attributes : x3::position_tagged
, std::map<std::string, quoted_string>
{
};
struct option_block : x3::position_tagged
{
std::vector<
#if 0
module_option
#else
x3::variant<lonely_comment, empty_line, module_option>
#endif
> items;
};
struct declaration : x3::position_tagged
, x3::variant<
lonely_comment,
empty_line,
global_var_decls,
const_var_stat,
vb_enum,
record,
//external_decl,
externalSub,
externalFunction,
eventHead
>
{
using base_type::base_type;
using base_type::operator=;
};
using functionList = std::vector<
x3::variant<//lonely_comment,
functionDef,
subDef
//get_prop,
//let_prop,
//set_prop
>
>;
struct vb_module : x3::position_tagged
{
module_attributes attrs;
option_block opts;
std::vector<declaration> declarations;
functionList functions;
};
} // STRICT_MODULE_STRUCTURE
struct declaration : x3::position_tagged
, x3::variant<
global_var_decls,
const_var_stat,
vb_enum,
record,
//external_decl,
externalSub,
externalFunction,
eventHead
>
{
using base_type::base_type;
using base_type::operator=;
};
using module_attribute = std::pair<std::string, std::string>;
using vb_module = std::vector<
x3::variant<
lonely_comment,
empty_line,
module_attribute,
module_option,
declaration,
functionDef,
subDef
//get_prop,
//let_prop,
//set_prop
>
>;
/*
struct vb_module : x3::position_tagged
{
std::vector<
x3::variant<
lonely_comment,
empty_line,
module_attribute,
module_option,
declaration,
functionDef,
subDef
//get_prop,
//let_prop,
//set_prop
>
> items;
};
*/
} // namespace vb6_ast

285
src/vb6_ast_adapt.hpp Normal file
View File

@@ -0,0 +1,285 @@
//: vb6_ast_adapt.hpp
// vb6_parser
// Copyright (c) 2022 Federico Aponte
// This code is licensed under GNU Software License (see LICENSE.txt for details)
#pragma once
#include "vb6_ast.hpp"
#include <boost/fusion/include/adapt_struct.hpp>
// these must be in the global scope!
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::integer_dec, val)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::integer_hex, val)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::integer_oct, val)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::long_dec, val)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::long_hex, val)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::long_oct, val)
// these are needed when BOOST_SPIRIT_X3_DEBUG is defined
#ifdef BOOST_SPIRIT_X3_DEBUG
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::nothing)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::empty_line)
#endif
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::lonely_comment,
content
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::identifier_context,
leading_dot,
elements
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::variable,
name,
construct,
//library_or_module,
type // not sure...
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::decorated_variable,
ctx,
var
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::func_call,
func_name,
params
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::global_var_decls,
at,
with_events,
vars
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::const_var,
var,
value
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::record,
at,
name,
members
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::vb_enum,
at,
name,
values
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::func_param,
isoptional,
qualifier, // byval, byref
var,
defvalue
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::statements::assignStmt,
type,
var,
rhs
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::statements::localVarDeclStmt,
type,
vars
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::statements::redimStmt,
preserve,
var,
newsize
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::statements::exitStmt,
type
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::statements::gotoStmt,
type,
label
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::statements::labelStmt,
label
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::statements::whileStmt,
condition,
block
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::statements::doStmt,
block
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::statements::dowhileStmt,
condition,
block
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::statements::loopwhileStmt,
block,
condition
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::statements::dountilStmt,
condition,
block
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::statements::loopuntilStmt,
block,
condition
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::statements::forStmt,
for_variable,
from,
to,
step,
block
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::statements::foreachStmt,
for_variable,
container,
block
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::statements::if_branch,
condition,
block
)
#ifdef SIMPLE_IF_STATEMENT
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::statements::ifelseStmt,
first_branch,
else_branch
)
#else
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::statements::ifelseStmt,
first_branch,
if_branches,
else_branch
)
#endif
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::statements::withStmt,
with_variable,
block
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::statements::case_block,
case_expr,
block
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::statements::selectStmt,
condition,
blocks
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::statements::onerrorStmt,
label,
type
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::statements::resumeStmt,
label_or_line_nr,
type
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::statements::callStmt,
sub_name,
params,
explicit_call
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::statements::raiseeventStmt,
event_name,
params
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::subHead,
at,
name,
params
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::eventHead,
at,
name,
params
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::functionHead,
at,
name,
params,
return_type // not sure...
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::propertyLetHead,
at,
name,
params
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::propertySetHead,
at,
name,
params
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::propertyGetHead,
at,
name,
params,
return_type // not sure...
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::subDef,
header,
statements
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::functionDef,
header,
statements
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::externalSub,
at,
name,
lib,
alias,
params
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::externalFunction,
at,
name,
lib,
alias,
params,
return_type // not sure...
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::STRICT_MODULE_STRUCTURE::option_block,
items
)
BOOST_FUSION_ADAPT_STRUCT(vb6_ast::STRICT_MODULE_STRUCTURE::vb_module,
attrs,
opts,
declarations,
functions
)

892
src/vb6_ast_printer.cpp Normal file
View File

@@ -0,0 +1,892 @@
//: vb6_ast_printer.cpp
// vb6_parser
// Copyright (c) 2022 Federico Aponte
// This code is licensed under GNU Software License (see LICENSE.txt for details)
#include "vb6_ast_printer.hpp"
#include <boost/variant/apply_visitor.hpp>
using namespace std;
// static
int vb6_ast_printer::indent_size = 4;
void vb6_ast_printer::operator()(vb6_ast::empty_line const&) const
{
os << '\n';
}
void vb6_ast_printer::operator()(vb6_ast::lonely_comment const& ast) const
{
os << string(indent, ' ');
os << "'" << ast.content << '\n';
}
void vb6_ast_printer::operator()(vb6_ast::identifier_context const& ast) const
{
if(ast.leading_dot)
os << '.';
struct {
void operator()(std::string s) { os << s; }
void operator()(vb6_ast::func_call const& s) { (*p)(s); }
ostream& os;
vb6_ast_printer const* p;
} visitor{ os, this };
for(auto& el : ast.elements)
{
boost::apply_visitor(visitor, el.get());
os << '.';
}
}
void vb6_ast_printer::operator()(vb6_ast::variable const& ast) const
{
os << ast.name;
if(ast.type)
{
os << " As ";
if(ast.construct)
os << "New ";
//if(ast.library_or_module)
// os << *ast.library_or_module << '.';
os << *ast.type;
}
//else
// os << " As <unspecified>";
}
void vb6_ast_printer::operator()(vb6_ast::decorated_variable const& ast) const
{
(*this)(ast.ctx);
os << ast.var;
}
void vb6_ast_printer::operator()(vb6_ast::func_call const& ast) const
{
os << ast.func_name << "(";
bool first = true;
for(auto& el : ast.params)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ")";
}
void vb6_ast_printer::operator()(vb6_ast::global_var_decls const& ast) const
{
print_type(ast.at);
bool first = true;
for(auto& el : ast.vars)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << '\n';
}
void vb6_ast_printer::operator()(vb6_ast::const_expr const& ast) const
{
struct {
void operator()(float v) { os << v << '!'; }
void operator()(double v) { os << v << '#'; }
void operator()(vb6_ast::long_dec v) { os << v.val << '&'; }
void operator()(vb6_ast::long_hex v) { os << "&H" << noshowbase << hex << v.val << dec << '&'; }
void operator()(vb6_ast::long_oct v) { os << "&0" << noshowbase << oct << v.val << dec << '&'; }
void operator()(vb6_ast::integer_dec v) { os << v.val << '%'; }
void operator()(vb6_ast::integer_hex v) { os << "&H" << noshowbase << hex << v.val << dec << '%'; }
void operator()(vb6_ast::integer_oct v) { os << "&0" << noshowbase << hex << v.val << dec << '%'; }
void operator()(bool v) { os << (v ? "True" : "False"); }
void operator()(vb6_ast::quoted_string const& s) { os << "\"" << s << "\""; }
void operator()(vb6_ast::nothing const&) { os << "Nothing"; }
ostream& os;
} visitor{os};
boost::apply_visitor(visitor, ast.get());
}
void vb6_ast_printer::operator()(vb6_ast::expression const& ast) const
{
boost::apply_visitor(*this, ast.get());
}
void vb6_ast_printer::operator()(vb6_ast::const_var_stat const& cvars) const
{
os << "Const ";
bool first = true;
for(auto& el : cvars)
{
if(first)
first = false;
else
os << ", ";
os << el.var.name;
//os << " As " << (el.var.type ? *el.var.type : "<unspecified>");
if(el.var.type)
os << " As " << *el.var.type;
os << " = ";
(*this)(el.value);
}
os << '\n';
}
void vb6_ast_printer::print_type(vb6_ast::access_type t) const
{
switch(t)
{
case vb6_ast::access_type::na: break;
case vb6_ast::access_type::dim: os << "Dim "; break;
case vb6_ast::access_type::private_: os << "Private "; break;
case vb6_ast::access_type::public_: os << "Public "; break;
case vb6_ast::access_type::global: os << "Global "; break;
case vb6_ast::access_type::friend_: os << "Friend "; break;
}
}
void vb6_ast_printer::operator()(vb6_ast::record const& ast) const
{
print_type(ast.at);
os << "Type " << ast.name << '\n';
indent += indent_size;
for(auto& el : ast.members)
{
os << string(indent, ' ');
(*this)(el);
os << '\n';
}
indent -= indent_size;
os << "End Type\n";
}
void vb6_ast_printer::operator()(vb6_ast::vb_enum const& ast) const
{
print_type(ast.at);
os << "Enum " << ast.name << '\n';
indent += indent_size;
for(auto& el : ast.values)
{
os << string(indent, ' ') << el.first;
if(el.second)
{
os << " = ";
(*this)(*el.second);
}
os << '\n';
}
indent -= indent_size;
os << "End Enum\n";
}
void vb6_ast_printer::operator()(vb6_ast::func_param const& ast) const
{
if(ast.isoptional)
os << "Optional ";
if(ast.qualifier)
os << (*ast.qualifier == vb6_ast::param_qualifier::byref ? "ByRef" : "ByVal") << " ";
os << ast.var.name;
//os << " As " << (ast.var.type ? *ast.var.type : "<unspecified>");
if(ast.var.type)
os << " As " << *ast.var.type;
if(ast.defvalue)
{
os << " = ";
(*this)(*ast.defvalue);
}
}
void vb6_ast_printer::operator()(vb6_ast::externalSub const& ast) const
{
print_type(ast.at);
os << "Declare Sub " << ast.name << " Lib \"" << ast.lib << "\" Alias \"" << ast.alias << "\" (";
bool first = true;
for(auto& el : ast.params)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ")\n";
}
void vb6_ast_printer::operator()(vb6_ast::subHead const& ast) const
{
print_type(ast.at);
os << "Sub " << ast.name << "(";
bool first = true;
for(auto& el : ast.params)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ")\n";
}
void vb6_ast_printer::operator()(vb6_ast::eventHead const& ast) const
{
print_type(ast.at);
os << "Event " << ast.name << "(";
bool first = true;
for(auto& el : ast.params)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ")\n";
}
void vb6_ast_printer::operator()(vb6_ast::functionHead const& ast) const
{
print_type(ast.at);
os << "Function " << ast.name << "(";
bool first = true;
for(auto& el : ast.params)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ")";
if(ast.return_type)
os << " As " << *ast.return_type;
os << '\n';
}
void vb6_ast_printer::operator()(vb6_ast::propertyLetHead const& ast) const
{
print_type(ast.at);
os << "Property Let " << ast.name << "(";
bool first = true;
for(auto& el : ast.params)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ")\n";
}
void vb6_ast_printer::operator()(vb6_ast::propertySetHead const& ast) const
{
print_type(ast.at);
os << "Property Set " << ast.name << "(";
bool first = true;
for(auto& el : ast.params)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ")\n";
}
void vb6_ast_printer::operator()(vb6_ast::propertyGetHead const& ast) const
{
print_type(ast.at);
os << "Property Get " << ast.name << "(";
bool first = true;
for(auto& el : ast.params)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ")";
if(ast.return_type)
os << " As " << *ast.return_type;
os << '\n';
}
void vb6_ast_printer::operator()(vb6_ast::subDef const& ast) const
{
(*this)(ast.header);
indent += indent_size;
for(auto& el : ast.statements)
{
(*this)(el);
}
indent -= indent_size;
os << "End Sub\n";
}
void vb6_ast_printer::operator()(vb6_ast::functionDef const& ast) const
{
(*this)(ast.header);
indent += indent_size;
for(auto& el : ast.statements)
{
(*this)(el);
}
indent -= indent_size;
os << "End Function\n";
}
void vb6_ast_printer::operator()(vb6_ast::externalFunction const& ast) const
{
print_type(ast.at);
os << "Declare Function " << ast.name << " Lib \"" << ast.lib << "\" Alias \"" << ast.alias << "\" (";
bool first = true;
for(auto& el : ast.params)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ")";
if(ast.return_type)
os << " As " << *ast.return_type;
os << '\n';
}
void vb6_ast_printer::operator()(vb6_ast::module_attribute attr) const
{
os << "Attribute " << attr.first << " = \"" << attr.second << "\"\n";
}
void vb6_ast_printer::operator()(vb6_ast::module_option opt) const
{
switch(opt)
{
case vb6_ast::module_option::explicit_:
os << "Option Explicit";
break;
case vb6_ast::module_option::base_0:
os << "Option Base 0";
break;
case vb6_ast::module_option::base_1:
os << "Option Base 1";
break;
case vb6_ast::module_option::compare_binary:
os << "Option Compare Binary";
break;
case vb6_ast::module_option::compare_text:
os << "Option Compare Text";
break;
case vb6_ast::module_option::private_module:
os << "Option Private Module";
break;
}
os << '\n';
}
void vb6_ast_printer::operator()(vb6_ast::STRICT_MODULE_STRUCTURE::module_attributes const& ast) const
{
for(auto& el : ast)
{
os << el.first << " = \"" << el.second << "\"\n";
}
}
void vb6_ast_printer::operator()(vb6_ast::STRICT_MODULE_STRUCTURE::declaration const& ast) const
{
boost::apply_visitor(*this, ast);
}
void vb6_ast_printer::operator()(vb6_ast::STRICT_MODULE_STRUCTURE::vb_module const& ast) const
{
(*this)(ast.attrs);
os << "'---------------------------------------- options\n";
for(auto& el : ast.opts.items)
{
#if 0
(*this)(el);
#else
boost::apply_visitor(*this, el);
#endif
}
os << "'---------------------------------------- declarations\n";
for(auto& el : ast.declarations)
{
(*this)(el);
}
os << "'---------------------------------------- functions\n";
for(auto& el : ast.functions)
{
boost::apply_visitor(*this, el);
}
}
void vb6_ast_printer::operator()(vb6_ast::declaration const& ast) const
{
boost::apply_visitor(*this, ast);
}
void vb6_ast_printer::operator()(vb6_ast::vb_module const& ast) const
{
for(auto& el : ast)
{
boost::apply_visitor(*this, el);
}
}
void vb6_ast_printer::operator()(vb6_ast::statements::assignStmt const& ast) const
{
os << string(indent, ' ');
switch(ast.type)
{
case vb6_ast::assignmentType::na: break;
case vb6_ast::assignmentType::set: os << "Set "; break;
case vb6_ast::assignmentType::let: os << "Let "; break;
}
(*this)(ast.var);
os << " = ";
(*this)(ast.rhs);
// print the index of the type in a comment
os << " ' " << ast.rhs.get().which() << '\n';
}
void vb6_ast_printer::operator()(vb6_ast::statements::exitStmt const& ast) const
{
os << string(indent, ' ') << "Exit ";
switch(ast.type)
{
case vb6_ast::exit_type::function: os << "Function"; break;
case vb6_ast::exit_type::sub: os << "Sub"; break;
case vb6_ast::exit_type::property: os << "Property"; break;
case vb6_ast::exit_type::while_: os << "While"; break;
case vb6_ast::exit_type::do_: os << "Do"; break;
case vb6_ast::exit_type::for_: os << "For"; break;
}
os << '\n';
}
void vb6_ast_printer::operator()(vb6_ast::statements::gotoStmt const& ast) const
{
os << string(indent, ' ') << (ast.type == vb6_ast::gotoType::goto_v ? "GoTo" : "GoSub")
<< " " << ast.label << '\n';
}
void vb6_ast_printer::operator()(vb6_ast::statements::onerrorStmt const& ast) const
{
os << string(indent, ' ') << "On Error ";
switch(ast.type)
{
case vb6_ast::onerror_type::goto_0: os << "GoTo 0"; break;
case vb6_ast::onerror_type::goto_neg_1: os << "GoTo -1"; break;
case vb6_ast::onerror_type::goto_label: os << "GoTo " << ast.label; break;
case vb6_ast::onerror_type::resume_next: os << "Resume Next"; break;
case vb6_ast::onerror_type::exit_sub: os << "Exit Sub"; break;
case vb6_ast::onerror_type::exit_func: os << "Exit Func"; break;
case vb6_ast::onerror_type::exit_property: os << "Exit Property"; break;
}
os << '\n';
}
void vb6_ast_printer::operator()(vb6_ast::statements::resumeStmt const& ast) const
{
os << string(indent, ' ') << "Resume";
switch(ast.type)
{
case vb6_ast::resume_type::implicit:
break;
case vb6_ast::resume_type::next:
os << " Next";
break;
case vb6_ast::resume_type::label:
os << " " << boost::get<string>(ast.label_or_line_nr);
break;
case vb6_ast::resume_type::line_nr:
os << " " << boost::get<int>(ast.label_or_line_nr);
break;
}
os << '\n';
}
void vb6_ast_printer::operator()(vb6_ast::statements::localVarDeclStmt const& ast) const
{
os << string(indent, ' ');
if(ast.type == vb6_ast::localvardeclType::Static)
os << "Static ";
else
os << "Dim ";
bool first = true;
for(auto& el : ast.vars)
{
if(first)
first = false;
else
os << ", ";
os << el.name;
if(el.type)
{
os << " As ";
if(el.construct)
os << "New ";
os << *el.type;
}
//else
// os << " As <unspecified>";
}
os << "\n";
}
void vb6_ast_printer::operator()(vb6_ast::statements::redimStmt const& ast) const
{
os << string(indent, ' ');
os << "ReDim ";
if(ast.preserve)
os << "Preserve ";
(*this)(ast.var);
os << "(";
bool first = true;
for(auto& el : ast.newsize)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ")\n";
}
// vb6_ast::returnStmt
void vb6_ast_printer::operator()(vb6_ast::statements::callStmt const& ast) const
{
os << string(indent, ' ');
if(ast.explicit_call)
os << "Call " << ast.sub_name << "(";
else
os << ast.sub_name << " ";
bool first = true;
for(auto& el : ast.params)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
if(ast.explicit_call)
os << ")";
os << "\n";
}
void vb6_ast_printer::operator()(vb6_ast::statements::raiseeventStmt const& ast) const
{
os << string(indent, ' ');
os << "RaiseEvent " << ast.event_name << "(";
bool first = true;
for(auto& el : ast.params)
{
if(first)
first = false;
else
os << ", ";
(*this)(el);
}
os << ")";
os << "\n";
}
void vb6_ast_printer::operator()(vb6_ast::statements::labelStmt const& ast) const
{
os << ast.label << ":\n"; // no indentation for labels
}
void vb6_ast_printer::operator()(vb6_ast::statements::whileStmt const& ast) const
{
os << string(indent, ' ') << "While ";
(*this)(ast.condition);
os << '\n';
indent += indent_size;
for(auto& st : ast.block)
(*this)(st);
indent -= indent_size;
os << string(indent, ' ') << "Wend\n";
}
void vb6_ast_printer::operator()(vb6_ast::statements::doStmt const& ast) const
{
os << string(indent, ' ') << "Do\n";
indent += indent_size;
for(auto& st : ast.block)
(*this)(st);
indent -= indent_size;
os << string(indent, ' ') << "Loop\n";
}
void vb6_ast_printer::operator()(vb6_ast::statements::dowhileStmt const& ast) const
{
os << string(indent, ' ') << "Do While ";
(*this)(ast.condition);
os << '\n';
indent += indent_size;
for(auto& st : ast.block)
(*this)(st);
indent -= indent_size;
os << string(indent, ' ') << "Loop\n";
}
void vb6_ast_printer::operator()(vb6_ast::statements::dountilStmt const& ast) const
{
os << string(indent, ' ') << "Do Until ";
(*this)(ast.condition);
os << '\n';
indent += indent_size;
for(auto& st : ast.block)
(*this)(st);
indent -= indent_size;
os << string(indent, ' ') << "Loop\n";
}
void vb6_ast_printer::operator()(vb6_ast::statements::loopwhileStmt const& ast) const
{
os << string(indent, ' ') << "Do\n";
indent += indent_size;
for(auto& st : ast.block)
(*this)(st);
indent -= indent_size;
os << string(indent, ' ') << "Loop While ";
(*this)(ast.condition);
os << '\n';
}
void vb6_ast_printer::operator()(vb6_ast::statements::loopuntilStmt const& ast) const
{
os << string(indent, ' ') << "Do\n";
indent += indent_size;
for(auto& st : ast.block)
(*this)(st);
indent -= indent_size;
os << string(indent, ' ') << "Loop Until ";
(*this)(ast.condition);
os << '\n';
}
void vb6_ast_printer::operator()(vb6_ast::statements::forStmt const& ast) const
{
os << string(indent, ' ') << "For ";
(*this)(ast.for_variable);
os << " = ";
(*this)(ast.from);
os << " To ";
(*this)(ast.to);
if(ast.step)
{
os << " Step ";
(*this)(*ast.step);
}
os << '\n';
indent += indent_size;
for(auto& el : ast.block)
(*this)(el);
indent -= indent_size;
os << string(indent, ' ') << "Next ";
(*this)(ast.for_variable);
os << '\n';
}
void vb6_ast_printer::operator()(vb6_ast::statements::foreachStmt const& ast) const
{
os << string(indent, ' ') << "For Each ";
(*this)(ast.for_variable);
os << " In ";
(*this)(ast.container);
os << '\n';
indent += indent_size;
for(auto& el : ast.block)
(*this)(el);
indent -= indent_size;
os << string(indent, ' ') << "Next ";
(*this)(ast.for_variable);
os << '\n';
}
void vb6_ast_printer::operator()(vb6_ast::statements::ifelseStmt const& ast) const
{
#ifdef SIMPLE_IF_STATEMENT
os << "If ";
(*this)(ast.first_branch.condition);
os << " Then\n";
// statements
indent += indent_size;
for(auto& st : ast.first_branch.block)
(*this)(st);
indent -= indent_size;
#else
os << "If ";
(*this)(ast.first_branch.condition);
os << " Then\n";
// statements
indent += indent_size;
for(auto& st : ast.first_branch.block)
(*this)(st);
indent -= indent_size;
//bool first = true;
for(auto& el : ast.if_branches)
{
os << string(indent, ' ');
//if(first)
//{
// os << "If ";
// first = false;
//}
//else
os << "ElseIf ";
(*this)(el.condition);
os << " Then\n";
indent += indent_size;
for(auto& st : el.block)
(*this)(st);
indent -= indent_size;
}
#endif
if(ast.else_branch.has_value())
{
os << string(indent, ' ') << "Else " << '\n';
indent += indent_size;
for(auto& el : ast.else_branch.get())
(*this)(el);
indent -= indent_size;
}
os << string(indent, ' ') << "End If\n";
}
void vb6_ast_printer::operator()(vb6_ast::statements::withStmt const& ast) const
{
os << string(indent, ' ') << "With ";
(*this)(ast.with_variable);
os << '\n';
// statements
indent += indent_size;
for(auto& st : ast.block)
(*this)(st);
indent -= indent_size;
os << string(indent, ' ') << "End With\n";
}
void vb6_ast_printer::operator()(vb6_ast::statements::case_block const& ast) const
{
// TODO
os << string(indent, ' ') << "Case ";
(*this)(ast.case_expr);
os << "\n";
indent += indent_size;
for(auto& st : ast.block)
(*this)(st);
indent -= indent_size;
}
void vb6_ast_printer::operator()(vb6_ast::statements::selectStmt const& ast) const
{
// TODO
os << string(indent, ' ') << "Select Case ";
(*this)(ast.condition);
os << '\n';
indent += indent_size;
for(auto& st : ast.blocks)
(*this)(st);
indent -= indent_size;
os << string(indent, ' ') << "End Case\n";
}
void vb6_ast_printer::operator()(vb6_ast::statements::singleStmt const& ast) const
{
//os << string(indent, ' '); // FED ???? wouldn't it be better to indent here rather than at each statement?
// => problem with some statements, e.g. else within the if-statement
// perhaps the else should be a statement of its own!!!
boost::apply_visitor(*this, ast.get());
}

81
src/vb6_ast_printer.hpp Normal file
View File

@@ -0,0 +1,81 @@
//: vb6_ast_printer.hpp
// vb6_parser
// Copyright (c) 2022 Federico Aponte
// This code is licensed under GNU Software License (see LICENSE.txt for details)
#pragma once
#include "vb6_ast.hpp"
#include <iostream>
class vb6_ast_printer
{
public:
explicit vb6_ast_printer(std::ostream& os) : os(os)
{
}
void operator()(vb6_ast::empty_line const&) const;
void operator()(vb6_ast::lonely_comment const&) const;
void operator()(vb6_ast::identifier_context const&) const;
void operator()(vb6_ast::variable const&) const;
void operator()(vb6_ast::decorated_variable const&) const;
void operator()(vb6_ast::const_expr const&) const;
void operator()(vb6_ast::expression const&) const;
void operator()(vb6_ast::func_call const&) const;
void operator()(vb6_ast::global_var_decls const&) const;
void operator()(vb6_ast::const_var_stat const&) const;
void operator()(vb6_ast::record const&) const;
void operator()(vb6_ast::vb_enum const&) const;
void operator()(vb6_ast::func_param const&) const;
void operator()(vb6_ast::externalSub const&) const;
void operator()(vb6_ast::externalFunction const&) const;
void operator()(vb6_ast::subHead const&) const;
void operator()(vb6_ast::eventHead const&) const;
void operator()(vb6_ast::functionHead const&) const;
void operator()(vb6_ast::propertyLetHead const&) const;
void operator()(vb6_ast::propertySetHead const&) const;
void operator()(vb6_ast::propertyGetHead const&) const;
void operator()(vb6_ast::subDef const&) const;
void operator()(vb6_ast::functionDef const&) const;
void operator()(vb6_ast::module_attribute) const;
void operator()(vb6_ast::module_option) const;
void operator()(vb6_ast::STRICT_MODULE_STRUCTURE::module_attributes const&) const;
void operator()(vb6_ast::STRICT_MODULE_STRUCTURE::declaration const&) const;
void operator()(vb6_ast::STRICT_MODULE_STRUCTURE::vb_module const&) const;
void operator()(vb6_ast::declaration const&) const;
void operator()(vb6_ast::vb_module const&) const;
void operator()(vb6_ast::statements::assignStmt const&) const;
void operator()(vb6_ast::statements::exitStmt const&) const;
void operator()(vb6_ast::statements::gotoStmt const&) const;
void operator()(vb6_ast::statements::onerrorStmt const&) const;
void operator()(vb6_ast::statements::resumeStmt const&) const;
void operator()(vb6_ast::statements::localVarDeclStmt const&) const;
void operator()(vb6_ast::statements::redimStmt const&) const;
void operator()(vb6_ast::statements::callStmt const&) const;
void operator()(vb6_ast::statements::raiseeventStmt const&) const;
void operator()(vb6_ast::statements::labelStmt const&) const;
void operator()(vb6_ast::statements::whileStmt const&) const;
void operator()(vb6_ast::statements::doStmt const&) const;
void operator()(vb6_ast::statements::dowhileStmt const&) const;
void operator()(vb6_ast::statements::loopwhileStmt const&) const;
void operator()(vb6_ast::statements::dountilStmt const&) const;
void operator()(vb6_ast::statements::loopuntilStmt const&) const;
void operator()(vb6_ast::statements::forStmt const&) const;
void operator()(vb6_ast::statements::foreachStmt const&) const;
void operator()(vb6_ast::statements::ifelseStmt const&) const;
void operator()(vb6_ast::statements::withStmt const&) const;
void operator()(vb6_ast::statements::case_block const&) const;
void operator()(vb6_ast::statements::selectStmt const&) const;
void operator()(vb6_ast::statements::singleStmt const&) const;
private:
void print_type(vb6_ast::access_type) const;
std::ostream& os;
mutable int indent = 0;
static int indent_size;
};

35
src/vb6_config.hpp Normal file
View File

@@ -0,0 +1,35 @@
//: vb6_config.hpp
// vb6_parser
// Copyright (c) 2022 Federico Aponte
// This code is licensed under GNU Software License (see LICENSE.txt for details)
#pragma once
#include "vb6_error_handler.hpp"
#include "vb6_parser.hpp" // only for having vb6_grammar::skip_type
#include <boost/spirit/home/x3.hpp>
#include <string>
namespace vb6_grammar {
namespace x3 = boost::spirit::x3;
using iterator_type = std::string_view::const_iterator;
//using iterator_type = std::string::const_iterator;
using phrase_context_type = x3::phrase_parse_context<skip_type>::type;
using error_handler_type = vb6_error_handler<iterator_type>;
//using error_handler_type = x3::unused_type;
using context_type = x3::context<vb6_error_handler_tag
, std::reference_wrapper<error_handler_type>
, phrase_context_type>;
//using context_type = x3::context<x3::skipper_tag
// , x3::char_class<boost::spirit::char_encoding::ascii, x3::blank_tag> const
// , x3::unused_type>;
}

42
src/vb6_error_handler.hpp Normal file
View File

@@ -0,0 +1,42 @@
//: vb6_error_handler.hpp
// vb6_parser
// Copyright (c) 2022 Federico Aponte
// This code is licensed under GNU Software License (see LICENSE.txt for details)
#pragma once
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/utility/error_reporting.hpp>
namespace vb6_grammar {
namespace x3 = boost::spirit::x3;
// x3::position_tagged
// annotation_base
// error_handler_base
// our error handler
template <typename Iterator>
using vb6_error_handler = x3::error_handler<Iterator>;
// tag used to get our error handler from the context
//using vb6_error_handler_tag = x3::error_handler_tag;
struct vb6_error_handler_tag;
#if 0
struct error_handler_base
{
template <typename Iterator, typename Exception, typename Context>
x3::error_handler_result on_error(Iterator& /*first*/, Iterator const& /*last*/,
Exception const& x, Context const& context)
{
std::string message = "Error! Expecting: " + x.which() + " here:";
auto& error_handler = x3::get<vb6_error_handler_tag>(context).get();
error_handler(x.where(), message);
return x3::error_handler_result::fail;
}
};
#endif
}

38
src/vb6_parser.cpp Normal file
View File

@@ -0,0 +1,38 @@
//: vb6_parser.cpp
// vb6_parser
// Copyright (c) 2022 Federico Aponte
// This code is licensed under GNU Software License (see LICENSE.txt for details)
#include "vb6_config.hpp"
#include "vb6_parser_def.hpp"
namespace vb6_grammar {
std::string getParserInfo();
/*
template bool parse_rule<iterator_type, context_type>(
????_type rule_
, iterator_type& first, iterator_type const& last
, context_type const& context, ????_type::attribute_type&);
*/
BOOST_SPIRIT_INSTANTIATE(empty_line_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(lonely_comment_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(quoted_string_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(basic_identifier_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(identifier_context_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(decorated_variable_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(simple_type_identifier_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(complex_type_identifier_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(type_identifier_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(single_var_declaration_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(global_var_declaration_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(record_declaration_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(const_expression_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(expression_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(enum_declaration_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(const_var_declaration_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(param_decl_type, iterator_type, context_type)
}

379
src/vb6_parser.hpp Normal file
View File

@@ -0,0 +1,379 @@
//: vb6_parser.hpp
// vb6_parser
// Copyright (c) 2022 Federico Aponte
// This code is licensed under GNU Software License (see LICENSE.txt for details)
#pragma once
#include "vb6_ast.hpp"
#include <boost/spirit/home/x3.hpp>
#include <iostream>
/*
----
Function return value
VB: The function's name treated as a variable is assigned the return value of
the function. No explicit return statement needed, though it can be used (Exit Function/Property).
C++: Explicit return value needed.
----
Macros
----
Comments
----
Multi-line statements, continuation symbol _
----
On Error GoTo/Resume
----
Exclamation mark operator
----
support ParamArray, Array
----
support AddressOf
support TypeOf
----
Implicit type variables
----
*/
namespace vb6_grammar {
std::string getParserInfo();
namespace x3 = boost::spirit::x3;
auto const kwRem = x3::no_case[x3::lit("Rem")];
auto const comment = x3::rule<class comment, std::string>("comment")
= (kwRem | '\'') >> x3::no_skip[*(x3::char_ - x3::eol)] >> x3::eol;
//= (kwRem | '\'') >> x3::seek[x3::eol];
#if 0
struct skip_class;
//using skip_type = x3::rule<skip_class, std::string>;
using skip_type = x3::rule<skip_class, x3::unused_type>;
skip_type const skip("vb6.skip");
//BOOST_SPIRIT_DECLARE(skip_type);
auto dbg_comment = [](auto& ctx) { std::cout << "comment: " << x3::_attr(ctx) << '\n'; };
// x3::blank only matches spaces or tabs, no newlines
auto const skip_def = x3::blank | (comment [dbg_comment]);
//= x3::blank | ('_' >> x3::eol) | comment;
BOOST_SPIRIT_DEFINE(skip)
#else
using skip_type = x3::ascii::blank_type;
skip_type const skip;
#endif
struct empty_line_class ;//: x3::annotation_base, error_handler_base {};
struct lonely_comment_class ;//: x3::annotate_on_success {};
struct quoted_string_class ;//: x3::annotate_on_success {};
struct basic_identifier_class ;//: x3::annotate_on_success {};
struct decorated_variable_class ;//: x3::annotate_on_success {};
struct identifier_context_class ;//: x3::annotate_on_success {};
struct simple_type_identifier_class ;//: x3::annotate_on_success {};
struct complex_type_identifier_class ;//: x3::annotate_on_success {};
struct type_identifier_class ;//: x3::annotate_on_success {};
struct single_var_declaration_class ;//: x3::annotate_on_success {};
struct global_var_declaration_class ;//: x3::annotate_on_success {};
struct record_declaration_class ;//: x3::annotate_on_success {};
struct const_expression_class ;//: x3::annotate_on_success {};
struct expression_class ;//: x3::annotate_on_success {};
struct enum_declaration_class ;//: x3::annotate_on_success {};
struct const_var_declaration_class ;//: x3::annotate_on_success {};
struct param_decl_class ;//: x3::annotate_on_success {};
struct external_sub_decl_class ;//: x3::annotate_on_success {};
struct external_function_decl_class ;//: x3::annotate_on_success {};
struct subHead_class ;//: x3::annotate_on_success {};
struct eventHead_class ;//: x3::annotate_on_success {};
struct functionHead_class ;//: x3::annotate_on_success {};
struct subDef_class ;//: x3::annotate_on_success {};
struct functionDef_class ;//: x3::annotate_on_success {};
struct propertyDef_class ;//: x3::annotate_on_success {};
struct functionCall_class ;//: x3::annotate_on_success {};
struct property_letHead_class ;//: x3::annotate_on_success {};
struct property_setHead_class ;//: x3::annotate_on_success {};
struct property_getHead_class ;//: x3::annotate_on_success {};
struct attributeDef_class ;//: x3::annotate_on_success {};
struct option_item_class ;//: x3::annotate_on_success {};
namespace statements {
struct singleStmt_class ;//: x3::annotate_on_success {};
struct statement_block_class ;//: x3::annotate_on_success {};
struct assignmentStmt_class ;//: x3::annotate_on_success {};
struct localvardeclStmt_class;//: x3::annotate_on_success {};
struct redimStmt_class ;//: x3::annotate_on_success {};
struct exitStmt_class ;//: x3::annotate_on_success {};
struct gotoStmt_class ;//: x3::annotate_on_success {};
struct onerrorStmt_class ;//: x3::annotate_on_success {};
struct resumeStmt_class ;//: x3::annotate_on_success {};
struct labelStmt_class ;//: x3::annotate_on_success {};
struct callimplicitStmt_class;//: x3::annotate_on_success {};
struct callexplicitStmt_class;//: x3::annotate_on_success {};
struct raiseeventStmt_class ;//: x3::annotate_on_success {};
// compound statements
struct whileStmt_class ;//: x3::annotate_on_success {};
struct doStmt_class ;//: x3::annotate_on_success {};
struct dowhileStmt_class ;//: x3::annotate_on_success {};
struct loopwhileStmt_class ;//: x3::annotate_on_success {};
struct dountilStmt_class ;//: x3::annotate_on_success {};
struct loopuntilStmt_class ;//: x3::annotate_on_success {};
struct forStmt_class ;//: x3::annotate_on_success {};
struct foreachStmt_class ;//: x3::annotate_on_success {};
struct ifelseStmt_class ;//: x3::annotate_on_success {};
struct withStmt_class ;//: x3::annotate_on_success {};
struct selectStmt_class ;//: x3::annotate_on_success {};
}
using empty_line_type = x3::rule<empty_line_class, vb6_ast::empty_line>;
using lonely_comment_type = x3::rule<lonely_comment_class, vb6_ast::lonely_comment>;
using quoted_string_type = x3::rule<quoted_string_class, vb6_ast::quoted_string>;
using basic_identifier_type = x3::rule<basic_identifier_class, vb6_ast::var_identifier>;
using decorated_variable_type = x3::rule<decorated_variable_class, vb6_ast::decorated_variable>;
using identifier_context_type = x3::rule<identifier_context_class, vb6_ast::identifier_context>;
using simple_type_identifier_type = x3::rule<simple_type_identifier_class, vb6_ast::simple_type_identifier>;
using complex_type_identifier_type = x3::rule<complex_type_identifier_class, vb6_ast::complex_type_identifier>;
using type_identifier_type = x3::rule<type_identifier_class, std::string>;
using single_var_declaration_type = x3::rule<single_var_declaration_class, vb6_ast::variable>;
using global_var_declaration_type = x3::rule<global_var_declaration_class, vb6_ast::global_var_decls>;
using record_declaration_type = x3::rule<record_declaration_class, vb6_ast::record>;
using const_expression_type = x3::rule<const_expression_class, vb6_ast::const_expr>;
using expression_type = x3::rule<expression_class, vb6_ast::expression>;
using enum_declaration_type = x3::rule<enum_declaration_class, vb6_ast::vb_enum>;
using const_var_declaration_type = x3::rule<const_var_declaration_class, vb6_ast::const_var_stat>;
using param_decl_type = x3::rule<param_decl_class, vb6_ast::func_param>;
using external_sub_decl_type = x3::rule<external_sub_decl_class, vb6_ast::externalSub>;
using external_function_decl_type = x3::rule<external_function_decl_class, vb6_ast::externalFunction>;
using subHead_type = x3::rule<subHead_class, vb6_ast::subHead>;
using eventHead_type = x3::rule<eventHead_class, vb6_ast::eventHead>;
using functionHead_type = x3::rule<functionHead_class, vb6_ast::functionHead>;
using property_letHead_type = x3::rule<property_letHead_class, vb6_ast::propertyLetHead>;
using property_setHead_type = x3::rule<property_setHead_class, vb6_ast::propertySetHead>;
using property_getHead_type = x3::rule<property_getHead_class, vb6_ast::propertyGetHead>;
using subDef_type = x3::rule<subDef_class, vb6_ast::subDef>;
using functionDef_type = x3::rule<functionDef_class, vb6_ast::functionDef>;
//using propertyDef_type = x3::rule<propertyDef_class, vb6_ast::propertyDef>;
using functionCall_type = x3::rule<functionCall_class, vb6_ast::func_call>;
using attributeDef_type = x3::rule<attributeDef_class, std::pair<std::string, vb6_ast::quoted_string>>;
using option_item_type = x3::rule<option_item_class, vb6_ast::module_option>;
namespace statements {
using singleStmt_type = x3::rule<singleStmt_class, vb6_ast::statements::singleStmt>;
using statement_block_type = x3::rule<statement_block_class, vb6_ast::statements::statement_block>;
using assignmentStmt_type = x3::rule<assignmentStmt_class, vb6_ast::statements::assignStmt>;
using localvardeclStmt_type = x3::rule<localvardeclStmt_class, vb6_ast::statements::localVarDeclStmt>;
using redimStmt_type = x3::rule<redimStmt_class, vb6_ast::statements::redimStmt>;
using exitStmt_type = x3::rule<exitStmt_class, vb6_ast::statements::exitStmt>;
using gotoStmt_type = x3::rule<gotoStmt_class, vb6_ast::statements::gotoStmt>;
using onerrorStmt_type = x3::rule<onerrorStmt_class, vb6_ast::statements::onerrorStmt>;
using resumeStmt_type = x3::rule<resumeStmt_class, vb6_ast::statements::resumeStmt>;
using labelStmt_type = x3::rule<labelStmt_class, vb6_ast::statements::labelStmt>;
using callimplicitStmt_type = x3::rule<callimplicitStmt_class, vb6_ast::statements::callStmt>;
using callexplicitStmt_type = x3::rule<callexplicitStmt_class, vb6_ast::statements::callStmt>;
using raiseeventStmt_type = x3::rule<raiseeventStmt_class, vb6_ast::statements::raiseeventStmt>;
// compound statements
using whileStmt_type = x3::rule<whileStmt_class, vb6_ast::statements::whileStmt>;
using doStmt_type = x3::rule<doStmt_class, vb6_ast::statements::doStmt>;
using dowhileStmt_type = x3::rule<dowhileStmt_class, vb6_ast::statements::dowhileStmt>;
using loopwhileStmt_type = x3::rule<loopwhileStmt_class, vb6_ast::statements::loopwhileStmt>;
using dountilStmt_type = x3::rule<dountilStmt_class, vb6_ast::statements::dountilStmt>;
using loopuntilStmt_type = x3::rule<loopuntilStmt_class, vb6_ast::statements::loopuntilStmt>;
using forStmt_type = x3::rule<forStmt_class, vb6_ast::statements::forStmt>;
using foreachStmt_type = x3::rule<foreachStmt_class, vb6_ast::statements::foreachStmt>;
using ifelseStmt_type = x3::rule<ifelseStmt_class, vb6_ast::statements::ifelseStmt>;
using withStmt_type = x3::rule<withStmt_class, vb6_ast::statements::withStmt>;
using selectStmt_type = x3::rule<selectStmt_class, vb6_ast::statements::selectStmt>;
using ifBranch_type = x3::rule<class ifBranch, vb6_ast::statements::if_branch>;
using elsifBranch_type = x3::rule<class elsifBranch, vb6_ast::statements::if_branch>;
using elseBranch_type = x3::rule<class elseBranch, vb6_ast::statements::statement_block>;
ifBranch_type const ifBranch("ifBranch");
elsifBranch_type const elsifBranch("elsifBranch");
elseBranch_type const elseBranch("elseBranch");
using case_block_type = x3::rule<class case_block, vb6_ast::statements::case_block>;
case_block_type const case_block("case_block");
}
namespace STRICT_MODULE_STRUCTURE
{
struct declaration_class; //: x3::annotation_base, error_handler_base {};
using declaration_type = x3::rule<declaration_class, vb6_ast::STRICT_MODULE_STRUCTURE::declaration>;
declaration_type const declaration("declaration");
struct basModDef_class; //: x3::annotation_base, error_handler_base {};
using basModDef_type = x3::rule<basModDef_class, vb6_ast::STRICT_MODULE_STRUCTURE::vb_module>;
basModDef_type const basModDef("basModDef");
BOOST_SPIRIT_DECLARE(
declaration_type
, basModDef_type
);
}
struct declaration_class; //: x3::annotation_base, error_handler_base {};
using declaration_type = x3::rule<class declaration, vb6_ast::declaration>;
declaration_type const declaration("declaration");
struct basModDef_class; //: x3::annotation_base, error_handler_base {};
using basModDef_type = x3::rule<basModDef_class, vb6_ast::vb_module>;
basModDef_type const basModDef("basModDef");
empty_line_type const empty_line ("empty_line");
lonely_comment_type const lonely_comment ("lonely_comment");
quoted_string_type const quoted_string ("quoted_string");
basic_identifier_type const basic_identifier ("basic_identifier");
decorated_variable_type const decorated_variable ("decorated_variable");
identifier_context_type const identifier_context ("identifier_context");
simple_type_identifier_type const simple_type_identifier ("simple_type_identifier");
complex_type_identifier_type const complex_type_identifier ("complex_type_identifier");
type_identifier_type const type_identifier ("type_identifier");
single_var_declaration_type const single_var_declaration ("single_var_declaration");
global_var_declaration_type const global_var_declaration ("global_var_declaration");
record_declaration_type const record_declaration ("record_declaration");
const_expression_type const const_expression ("const_expression");
expression_type const expression ("expression");
enum_declaration_type const enum_declaration ("enum_declaration");
const_var_declaration_type const const_var_declaration ("const_var_declaration");
param_decl_type const param_decl ("param_decl");
external_sub_decl_type const external_sub_decl ("external_sub_decl");
external_function_decl_type const external_function_decl ("external_function_decl");
subHead_type const subHead ("subHead");
eventHead_type const eventHead ("eventHead");
functionHead_type const functionHead ("functionHead");
property_letHead_type const property_letHead ("property_letHead");
property_setHead_type const property_setHead ("property_setHead");
property_getHead_type const property_getHead ("property_getHead");
subDef_type const subDef ("subDef");
functionDef_type const functionDef ("functionDef");
//propertyDef_type const propertyDef ("propertyDef");
functionCall_type const functionCall ("functionCall");
attributeDef_type const attributeDef ("attributeDef");
option_item_type const option_item ("option_item");
namespace statements {
singleStmt_type const singleStmt ("singleStmt");
statement_block_type const statement_block ("statement_block");
assignmentStmt_type const assignmentStmt ("assignmentStmt");
localvardeclStmt_type const localvardeclStmt ("localvardeclStmt");
redimStmt_type const redimStmt ("redimStmt");
exitStmt_type const exitStmt ("exitStmt");
gotoStmt_type const gotoStmt ("gotoStmt");
onerrorStmt_type const onerrorStmt ("onerrorStmt");
resumeStmt_type const resumeStmt ("resumeStmt");
labelStmt_type const labelStmt ("labelStmt");
callimplicitStmt_type const callimplicitStmt ("callimplicitStmt");
callexplicitStmt_type const callexplicitStmt ("callexplicitStmt");
raiseeventStmt_type const raiseeventStmt ("raiseeventStmt");
// compound statements
whileStmt_type const whileStmt ("whileStmt");
doStmt_type const doStmt ("doStmt");
dowhileStmt_type const dowhileStmt ("dowhileStmt");
loopwhileStmt_type const loopwhileStmt ("loopwhileStmt");
dountilStmt_type const dountilStmt ("dountilStmt");
loopuntilStmt_type const loopuntilStmt ("loopuntilStmt");
forStmt_type const forStmt ("forStmt");
foreachStmt_type const foreachStmt ("foreachStmt");
ifelseStmt_type const ifelseStmt ("ifelseStmt");
withStmt_type const withStmt ("withStmt");
selectStmt_type const selectStmt ("selectStmt");
}
auto const var_identifier = basic_identifier;
auto const record_identifier = basic_identifier;
auto const enum_identifier = basic_identifier;
auto const sub_identifier = basic_identifier;
auto const func_identifier = basic_identifier;
auto const event_identifier = basic_identifier;
auto const prop_identifier = basic_identifier;
auto const lib_name = quoted_string;
auto const alias_name = quoted_string;
/*
template <typename Iterator, typename Context>
bool parse_rule(
????_type rule_
, Iterator& first, Iterator const& last
, Context const& context, ????_type::attribute_type& attr);
*/
BOOST_SPIRIT_DECLARE(
empty_line_type
, lonely_comment_type
, quoted_string_type
, basic_identifier_type
, identifier_context_type
, decorated_variable_type
, simple_type_identifier_type
, complex_type_identifier_type
, type_identifier_type
, single_var_declaration_type
, global_var_declaration_type
, record_declaration_type
, const_expression_type
, expression_type
, enum_declaration_type
, const_var_declaration_type
, param_decl_type
, external_sub_decl_type
, external_function_decl_type
, subHead_type
, eventHead_type
, functionHead_type
, property_letHead_type
, property_setHead_type
, property_getHead_type
, subDef_type
, functionDef_type
//, propertyDef_type
, functionCall_type
, attributeDef_type
, option_item_type
, declaration_type
, basModDef_type
)
BOOST_SPIRIT_DECLARE(
statements::singleStmt_type
, statements::statement_block_type
, statements::assignmentStmt_type
, statements::localvardeclStmt_type
, statements::redimStmt_type
, statements::exitStmt_type
, statements::gotoStmt_type
, statements::onerrorStmt_type
, statements::resumeStmt_type
, statements::labelStmt_type
, statements::callimplicitStmt_type
, statements::callexplicitStmt_type
, statements::raiseeventStmt_type
)
BOOST_SPIRIT_DECLARE(
statements::ifBranch_type
, statements::elsifBranch_type
, statements::elseBranch_type
, statements::case_block_type
)
// compound statements
BOOST_SPIRIT_DECLARE(
statements::whileStmt_type
, statements::doStmt_type
, statements::dowhileStmt_type
, statements::loopwhileStmt_type
, statements::dountilStmt_type
, statements::loopuntilStmt_type
, statements::forStmt_type
, statements::foreachStmt_type
, statements::ifelseStmt_type
, statements::withStmt_type
, statements::selectStmt_type
)
}

475
src/vb6_parser_def.hpp Normal file
View File

@@ -0,0 +1,475 @@
//: vb6_parser_def.hpp
// vb6_parser
// Copyright (c) 2022 Federico Aponte
// This code is licensed under GNU Software License (see LICENSE.txt for details)
#pragma once
#include "vb6_parser.hpp"
#include "vb6_ast_adapt.hpp"
#include "vb6_parser_keywords.hpp"
#include "vb6_parser_operators.hpp"
#include <boost/fusion/include/std_pair.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/utility/annotate_on_success.hpp>
// http://boost.2283326.n4.nabble.com/Horrible-compiletimes-and-memory-usage-while-compiling-a-parser-with-X3-td4689104.html
/*
----
Function return value
VB: The function's name treated as a variable is assigned the return value of
the function. No explicit return statement needed, though it can be used (Exit Function/Property).
C++: Explicit return value needed.
----
Macros
----
Comments
----
Multi-line statements, continuation symbol _
----
On Error GoTo/Resume
----
Exclamation mark operator
----
support ParamArray, Array
----
support AddressOf
----
Implicit type variables
----
*/
namespace vb6_grammar {
namespace x3 = boost::spirit::x3;
// the terminator for every VB6 statement
auto const cmdTermin = x3::eol | ':';
// keyword groups
auto const kwgEndType = kwEnd > kwType > cmdTermin;
auto const kwgEndEnum = kwEnd > kwEnum > cmdTermin;
auto const kwgEndFunction = kwEnd > kwFunction > cmdTermin;
auto const kwgEndSub = kwEnd > kwSub > cmdTermin;
auto const kwgEndProperty = kwEnd > kwProperty > cmdTermin;
auto const bool_const = x3::rule<class bool_const, bool>("bool_const")
= (kwTrue >> x3::attr(true))
| (kwFalse >> x3::attr(false));
auto const keywords = kwFor | kwEach | kwStep | kwTo | kwNext | kwEnd
| kwWhile | kwWend | kwLoop | kwDo | kwUntil
| kwIf | kwElse | kwElseIf | kwExit | kwSelect | kwCase
| kwPublic| kwPrivate | kwGlobal | kwDim
| kwGet | kwSet | kwLet
| kwOn | kwLocal | kwGoTo | kwGoSub | kwReturn
| kwCall | kwRaiseEvent
| kwSub | kwFunction | kwEvent | kwProperty
| kwByVal | kwByRef | kwAs | kwIn | kwOption;
auto const reserved = keywords >> !x3::char_("a-zA-Z0-9_£");
//auto const reserved = keywords >> (x3::char_ - x3::char_("a-zA-Z0-9_£"));
// FED ???? dovrebbe andare bene, rivedere e pulire
auto const empty_line_def = //x3::omit[x3::no_skip[*x3::blank]]
//>> x3::attr(vb6_ast::empty_line()) >> x3::eol;
x3::omit[x3::no_skip[*x3::blank >> x3::eol]]
>> x3::attr(vb6_ast::empty_line());
// line composed only of a comment, no VB6 code
//auto const lonely_comment_def = x3::no_skip[ x3::omit[*x3::blank]
// >> (kwRem | x3::lit('\''))
// >> *(x3::char_ - x3::eol)] >> x3::eol;
// FED ???? dovrebbe andare bene, rivedere e pulire
auto const lonely_comment_def = x3::no_skip[ x3::omit[*x3::blank]
>> (kwRem | x3::lit('\''))
>> *(x3::char_ - x3::eol) >> x3::eol];
// FED ???? the expression ~x3::char_('"') does not behave as I expect
//auto const quoted_string_def = x3::lexeme['"' >> *(~x3::char_('"')) >> '"'];
auto const quoted_string_def = x3::lexeme['"' >> *(x3::char_ - '"') >> '"'];
// need to remove the reserved words from the possible identifiers
auto const basic_identifier_def = x3::lexeme[x3::no_case[x3::char_("a-zA-Z_£") >> *x3::char_("a-zA-Z0-9_£")] - reserved];
//= x3::lexeme[x3::no_case[(x3::alpha | '_' | '£') >> *(x3::alnum | '_' | '£')] - reserved];
/*
Dim x As String ' simple identifier, variable name
x.y.z = 5 ' composed, variable name
Call foo(x.y.z) ' composed, variable name
Enum types : a = 1 : b = 2 : End Enum ' simple, type name
Type point : x As Double : y As Double : End Type ' simple, type name
Dim x As Module.types ' composed, type name
Call Module.foo() ' composed, sub name
Call .foo() ' composed, sub name, with-statement
a = GetValue().x ' composed
*/
auto const identifier_context_def = -(opDot >> x3::attr(true))
>> *((functionCall | basic_identifier) >> opDot);
auto const param_qualifier = kwByVal | kwByRef;
auto const simple_type_identifier_def = //x3::attr(boost::optional<std::string>()) >>
( kwBoolean
| kwByte
| kwInteger
| kwLong
| kwSingle
| kwDouble
| kwCurrency
| kwDate
| kwString
| kwObject
| kwVariant);
/*
auto const simple_type_identifier_def = (kwBoolean >> x3::attr(vb6_ast::NativeType::Boolean))
| (kwByte >> x3::attr(vb6_ast::NativeType::Byte))
| (kwInteger >> x3::attr(vb6_ast::NativeType::Integer))
| (kwLong >> x3::attr(vb6_ast::NativeType::Long))
| (kwSingle >> x3::attr(vb6_ast::NativeType::Single))
| (kwDouble >> x3::attr(vb6_ast::NativeType::Double))
| (kwCurrency >> x3::attr(vb6_ast::NativeType::Currency))
| (kwDate >> x3::attr(vb6_ast::NativeType::Date))
| (kwString >> x3::attr(vb6_ast::NativeType::String))
| (kwObject >> x3::attr(vb6_ast::NativeType::Object))
| (kwVariant >> x3::attr(vb6_ast::NativeType::Variant));
*/
auto const complex_type_identifier_def = x3::omit[-(basic_identifier >> opDot)] >> basic_identifier;
auto const type_identifier_def = (simple_type_identifier | complex_type_identifier);
auto const array_size_decl = x3::omit["(" >> x3::int_ >> ")"];
auto const tmp //= x3::rule<class tmp_class, std::tuple<bool, boost::optional<vb6_ast::simple_type_identifier>>>()
= (kwAs >> kwNew >> x3::attr(true) >> type_identifier);
//| (kwAs >> x3::attr(false) >> type_identifier)
//| (x3::attr(false) >> x3::attr(boost::none));
auto const tmp1 //= x3::rule<class tmp1_class, std::tuple<bool, boost::optional<vb6_ast::simple_type_identifier>>>()
= kwAs >> kwNew >> x3::attr(true) >> simple_type_identifier;
auto const tmp2 //= x3::rule<class tmp2_class, std::tuple<bool, boost::optional<vb6_ast::simple_type_identifier>>>()
= kwAs >> x3::attr(false) >> simple_type_identifier;
auto const single_var_implicit_declaration_def = var_identifier >> -array_size_decl >> x3::attr(false) >> x3::attr(boost::none);
auto const single_var_declaration_def = var_identifier >> -array_size_decl >> (tmp1);
//auto const single_var_declaration_def = var_identifier >> -array_size_decl >> (tmp1 | tmp2);
//auto const single_var_declaration_def = var_identifier >> -array_size_decl >> -(kwAs >> -(kwNew >> x3::attr(true)) >> type_identifier);
auto const global_var_declaration_def = ( (kwDim >> x3::attr(vb6_ast::access_type::dim))
| (kwGlobal >> x3::attr(vb6_ast::access_type::global))
| (kwPublic >> x3::attr(vb6_ast::access_type::public_))
| (kwPrivate >> x3::attr(vb6_ast::access_type::private_))
) >> -(kwWithEvents >> x3::attr(true)) >> (single_var_declaration % ',') >> cmdTermin;
auto const private_or_public = (kwPrivate >> x3::attr(vb6_ast::access_type::private_))
| (kwPublic >> x3::attr(vb6_ast::access_type::public_))
| (x3::eps >> x3::attr(vb6_ast::access_type::na));
auto const record_declaration_def = private_or_public >> kwType >> record_identifier >> cmdTermin
//>> (single_var_declaration % cmdTermin) // this fails, but why?
>> +(single_var_declaration >> cmdTermin)
>> kwgEndType;
auto const single_float = x3::rule<class single_float, float>("single_float")
= x3::lexeme[x3::float_ >> '!'];
auto const double_float = x3::rule<class double_float, double>("double_float")
= x3::lexeme[x3::double_ >> '#'];
auto const long_dec = x3::rule<class long_dec, vb6_ast::long_dec>("long_dec")
= x3::lexeme[x3::int_ >> '&'];
auto const long_hex = x3::rule<class long_hex, vb6_ast::long_hex>("long_hex")
= x3::lexeme["&H" >> x3::hex >> '&'];
auto const long_oct = x3::rule<class long_oct, vb6_ast::long_oct>("long_oct")
= x3::lexeme["&0" >> x3::oct >> '&'];
auto const integer_dec = x3::rule<class integer_dec, vb6_ast::integer_dec>("integer_dec")
= x3::lexeme[x3::short_ >> '%']
| x3::short_;
auto const integer_hex = x3::rule<class integer_hex, vb6_ast::integer_hex>("integer_hex")
= x3::lexeme["&H" >> x3::hex >> '%'];
auto const integer_oct = x3::rule<class integer_oct, vb6_ast::integer_oct>("integer_oct")
= x3::lexeme["&0" >> x3::oct >> '%'];
auto const currency = x3::rule<class currency, double>("currency")
= x3::lexeme[x3::double_ >> '@'];
x3::real_parser<float, x3::strict_real_policies<float>> const float_ = {};
//x3::real_parser<double, x3::strict_real_policies<double>> const double_ = {};
auto const const_expression_def = double_float
| single_float
| float_
| long_dec
| long_hex
| long_oct
| integer_dec
| integer_hex
| integer_oct
| quoted_string
| bool_const
| (kwNothing >> x3::attr(vb6_ast::nothing()));
namespace expr_take_1
{
struct factor_class;
struct term_class;
using factor_type = x3::rule<factor_class>;
using term_type = x3::rule<term_class>;
factor_type const factor("factor");
term_type const term("term");
auto const strong_op = opMult | opDiv | opDivint | opMod | opAnd;
auto const weak_op = opPlus | opMinus | opOr;
auto const term_def = factor >> *(strong_op >> factor);
auto const factor_def = '(' >> expression >> ')'
| const_expression
| functionCall // recursive
| decorated_variable
| (opNot >> factor);
auto const simpleExpression = -(opPlus|opMinus) >> term >> *(weak_op >> term);
BOOST_SPIRIT_DEFINE(
factor
, term
)
} // namespace expr_take_1
namespace expr_take_2
{
// https://levelup.gitconnected.com/create-your-own-expression-parser-d1f622077796
// https://panthema.net/2018/0912-Boost-Spirit-Tutorial/
struct expr_class;
struct mulexpr_class;
struct powexpr_class;
struct atom_class;
using expr_type = x3::rule<expr_class>;
using mulexpr_type = x3::rule<mulexpr_class>;
using powexpr_type = x3::rule<powexpr_class>;
using atom_type = x3::rule<atom_class>;
expr_type const expr("expr");
mulexpr_type const mulexpr("mulexpr");
powexpr_type const powexpr("powexpr");
atom_type const atom("atom");
auto const addop = opPlus | opMinus;
auto const mulop = opMult | opDiv;
// expression | RPN
// ------------+---------------
// 2+3*4 | 2 3 4 * +
// 2*3+4 | 2 3 * 4 +
// (2+3)*4 | 2 3 + 4 *
// 2^3*4+5 | 2 3 ^ 4 * 5 +
// 2+3*4^5 | 2 3 4 5 ^ * +
auto const expr_def = mulexpr >> *(addop >> mulexpr);
auto const mulexpr_def = powexpr >> *(mulop >> powexpr);
auto const powexpr_def = (-(opPlus|opMinus) >> powexpr)
| (atom >> -("^" >> powexpr));
auto const atom_def = functionCall
| const_expression
| ("(" >> expr >> ")");
BOOST_SPIRIT_DEFINE(
expr
, mulexpr
, powexpr
, atom
)
} // namespace expr_take_3
auto const decorated_variable_def = identifier_context >> var_identifier;
//auto const decorated_functionCall = x3::omit[identifier_context] >> functionCall;
auto const expression_def = const_expression
| functionCall
| decorated_variable
;
auto const enum_declaration_def = private_or_public >> kwEnum >> enum_identifier >> cmdTermin
>> +(basic_identifier >> -(opEqual >> const_expression) >> cmdTermin) // FED ???? basic_identifier?
>> kwgEndEnum;
auto const const_var_declaration_def = -kwPrivate >> kwConst
>> ((single_var_declaration >> opEqual >> const_expression) % ',')
>> cmdTermin;
auto const param_decl_def = -(kwOptional >> x3::attr(true)) >> -param_qualifier
>> single_var_declaration // FED ??? this can have 'New', change it
>> -(opEqual >> const_expression); // default value for optional parameter
// helper definition
auto const param_list_decl = -(param_decl % ',');
auto const external_sub_decl_def = private_or_public >> kwDeclare >> kwSub >> sub_identifier
>> kwLib >> lib_name >> -(kwAlias >> alias_name)
>> '(' >> param_list_decl >> ')'
>> cmdTermin;
auto const external_function_decl_def = private_or_public >> kwDeclare >> kwFunction >> func_identifier
>> kwLib >> lib_name >> -(kwAlias >> alias_name)
>> '(' >> param_list_decl >> ')' >> -(kwAs >> type_identifier)
>> cmdTermin;
auto const subHead_def = private_or_public >> kwSub
>> sub_identifier
>> '(' >> param_list_decl >> ')'
>> cmdTermin;
auto const eventHead_def = private_or_public >> kwEvent
>> sub_identifier
>> '(' >> param_list_decl >> ')'
>> cmdTermin;
auto const functionHead_def = private_or_public >> kwFunction >> func_identifier
>> '(' >> param_list_decl >> ')' >> -(kwAs >> type_identifier)
>> cmdTermin;
auto const property_letHead_def = private_or_public >> (kwProperty > kwLet) >> prop_identifier
>> '(' >> param_list_decl >> ')'
>> cmdTermin;
auto const property_setHead_def = private_or_public >> (kwProperty > kwSet) >> prop_identifier
>> '(' >> param_list_decl >> ')'
>> cmdTermin;
auto const property_getHead_def = private_or_public >> (kwProperty > kwGet) >> prop_identifier
>> '(' >> param_list_decl >> ')' >> -(kwAs >> type_identifier)
>> cmdTermin;;
auto const functionCall_def = func_identifier >> '(' >> -(expression % ',') >> ')';
//auto const decorated_functionCall = x3::omit[identifier_context] >> functionCall;
//auto const decorated_sub_identifier = x3::omit[identifier_context] >> sub_identifier;
// these are defined after statement_block as they depend on it
auto const subDef_def = subHead
>> statements::statement_block
>> kwgEndSub;
auto const functionDef_def = functionHead
>> statements::statement_block
>> kwgEndFunction;
//auto const propertyDef_def = (property_getHead | property_letHead | property_setHead)
// >> statements::statement_block
// >> kwgEndProperty;
auto const attr_name = basic_identifier;
auto const attributeDef_def = kwAttribute
>> attr_name >> opEqual >> quoted_string
>> cmdTermin;
auto const option_item_def = kwOption > ( (kwExplicit >> x3::attr(vb6_ast::module_option::explicit_))
| (kwCompare > ((kwText >> x3::attr(vb6_ast::module_option::compare_text)) |
(kwBinary >> x3::attr(vb6_ast::module_option::compare_binary))))
| (kwBase > ((x3::lit('0') >> x3::attr(vb6_ast::module_option::base_0)) |
(x3::lit('1') >> x3::attr(vb6_ast::module_option::base_1))))
| ((kwPrivate > kwModule) >> x3::attr(vb6_ast::module_option::private_module))
) >> cmdTermin;
namespace STRICT_MODULE_STRUCTURE
{
#if 0
auto const option_block = *option_item;
#else
auto const option_block = x3::rule<class option_block, vb6_ast::STRICT_MODULE_STRUCTURE::option_block>()
= -( *(lonely_comment | empty_line | option_item)
//>> +option_item
);
#endif
auto const preamble = *(attributeDef)
>> option_block;
auto const declaration_def = lonely_comment // critical to have this as the first element
| empty_line
| global_var_declaration
| const_var_declaration
| enum_declaration
| record_declaration
| external_sub_decl
| external_function_decl
| eventHead
;
//auto const funcList = x3::rule<class funcList, vb6_ast::functionList>("funcList")
// = *( subDef
// | functionDef
// //| propertyDef
// );
auto const func_subDef = subDef
| functionDef
//| propertyDef
;
auto const basModDef_def = preamble
>> (*declaration)
>> (*func_subDef);
auto const clsModDef = preamble >> *declaration >> *func_subDef;
auto const frmModDef = preamble >> /*formDef >>*/ *declaration >> *func_subDef;
auto const ctlModDef = preamble >> /*formDef >>*/ *declaration >> *func_subDef;
auto const unitDef = basModDef | clsModDef | frmModDef | ctlModDef;
BOOST_SPIRIT_DEFINE(
declaration
, basModDef
)
} // namespace STRICT_MODULE_STRUCTURE
auto const declaration_def = global_var_declaration
| const_var_declaration
| enum_declaration
| record_declaration
| external_sub_decl
| external_function_decl
| eventHead
;
auto const func_subDef = subDef
| functionDef
//| propertyDef
;
auto const basModDef_def = *(lonely_comment // critical to have this as the first element
| empty_line
| attributeDef
| option_item
| declaration
| func_subDef);
auto const unitDef = basModDef;
BOOST_SPIRIT_DEFINE(
empty_line
, lonely_comment
, quoted_string
, basic_identifier
, identifier_context
, decorated_variable
, simple_type_identifier
, complex_type_identifier
, type_identifier
, single_var_declaration
, global_var_declaration
, record_declaration
, const_expression
, expression
, enum_declaration
, const_var_declaration
, param_decl
, external_sub_decl
, external_function_decl
, subHead
, eventHead
, functionHead
, property_letHead
, property_setHead
, property_getHead
, subDef
, functionDef
, functionCall
, attributeDef
, option_item
, declaration
, basModDef
)
//, propertyDef
}

View File

@@ -0,0 +1,35 @@
//: vb6_parser_functions.cpp
// vb6_parser
// Copyright (c) 2022 Federico Aponte
// This code is licensed under GNU Software License (see LICENSE.txt for details)
#include "vb6_config.hpp"
#include "vb6_parser_def.hpp"
namespace vb6_grammar {
/*
template bool parse_rule<iterator_type, context_type>(
????_type rule_
, iterator_type& first, iterator_type const& last
, context_type const& context, ????_type::attribute_type&);
*/
BOOST_SPIRIT_INSTANTIATE(external_sub_decl_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(external_function_decl_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(subHead_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(eventHead_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(functionHead_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(property_letHead_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(property_setHead_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(property_getHead_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(functionCall_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(declaration_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(attributeDef_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(option_item_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(subDef_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(functionDef_type, iterator_type, context_type)
//BOOST_SPIRIT_INSTANTIATE(propertyDef_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(basModDef_type, iterator_type, context_type)
}

23
src/vb6_parser_helper.cpp Normal file
View File

@@ -0,0 +1,23 @@
//: vb6_parser_helper.cpp
// vb6_parser
// Copyright (c) 2022 Federico Aponte
// This code is licensed under GNU Software License (see LICENSE.txt for details)
#include <boost/spirit/home/x3/version.hpp>
#include <sstream>
namespace vb6_grammar {
std::string getParserInfo()
{
using namespace std;
ostringstream os;
os << "SPIRIT_X3_VERSION: " << showbase << hex << SPIRIT_X3_VERSION
<< noshowbase << dec << '\n'
<< "VB6_PARSER_VERSION: 0.1" << '\n';
return os.str();
}
}

153
src/vb6_parser_keywords.hpp Normal file
View File

@@ -0,0 +1,153 @@
//: vb6_parser_keywords.hpp
// vb6_parser
// Copyright (c) 2022 Federico Aponte
// This code is licensed under GNU Software License (see LICENSE.txt for details)
#pragma once
#include <boost/spirit/home/x3.hpp>
namespace vb6_grammar {
namespace x3 = boost::spirit::x3;
// constants
auto const kwTrue = x3::no_case[x3::lit("True")];
auto const kwFalse = x3::no_case[x3::lit("False")];
auto const kwNothing = x3::no_case[x3::lit("Nothing")];
// primitive types
auto const kwBoolean = x3::rule<class kwBoolean, std::string>("kwBoolean")
= x3::no_case[x3::lit("Boolean")];
auto const kwByte = x3::rule<class kwByte, std::string>("kwByte")
= x3::no_case[x3::lit("Byte")];
auto const kwInteger = x3::rule<class kwInteger, std::string>("kwInteger")
= x3::no_case[x3::lit("Integer")];
auto const kwLong = x3::rule<class kwLong, std::string>("kwLong")
= x3::no_case[x3::lit("Long")];
auto const kwSingle = x3::rule<class kwSingle, std::string>("kwSingle")
= x3::no_case[x3::lit("Single")];
auto const kwDouble = x3::rule<class kwDouble, std::string>("kwDouble")
= x3::no_case[x3::lit("Double")];
auto const kwCurrency = x3::rule<class kwCurrency, std::string>("kwCurrency")
= x3::no_case[x3::lit("Currency")];
auto const kwDate = x3::rule<class kwDate, std::string>("kwDate")
= x3::no_case[x3::lit("Date")];
auto const kwString = x3::rule<class kwString, std::string>("kwString")
= x3::no_case[x3::lit("String")];
auto const kwObject = x3::rule<class kwObject, std::string>("kwObject")
= x3::no_case[x3::lit("Object")];
auto const kwVariant = x3::rule<class kwVariant, std::string>("kwVariant")
= x3::no_case[x3::lit("Variant")];
// only for external functions and subroutines
// "As Any" disables type checking and allows any data type to be passed in or returned
auto const kwAny = x3::rule<class kwAny, std::string>("kwAny")
= x3::no_case[x3::lit("Any")];
// VB6 keywords
auto const kwLet = x3::no_case[x3::lit("Let")];
auto const kwSet = x3::no_case[x3::lit("Set")];
auto const kwGet = x3::no_case[x3::lit("Get")]; // also for file handling
auto const kwRSet = x3::no_case[x3::lit("RSet")];
//auto const kwBegin = x3::no_case[x3::lit("Begin")];
auto const kwEnd = x3::no_case[x3::lit("End")];
auto const kwOn = x3::no_case[x3::lit("On")];
auto const kwLocal = x3::no_case[x3::lit("Local")];
auto const kwError = x3::no_case[x3::lit("Error")];
auto const kwResume = x3::no_case[x3::lit("Resume")];
auto const kwEvent = x3::no_case[x3::lit("Event")];
auto const kwProperty = x3::no_case[x3::lit("Property")];
auto const kwParamArray = x3::rule<class kwParamArray, std::string>("kwParamArray")
= x3::no_case[x3::lit("ParamArray")];
auto const kwDefault = x3::rule<class kwDefault, std::string>("kwDefault")
= x3::no_case[x3::lit("Default")];
auto const kwAddressOf = x3::rule<class kwAddressOf, std::string>("kwAddressOf")
= x3::no_case[x3::lit("AddressOf")];
auto const kwStatic = x3::no_case[x3::lit("Static")];
auto const kwFriend = x3::no_case[x3::lit("Friend")]; // used only in class modules
auto const kwIf = x3::no_case[x3::lit("If")];
auto const kwThen = x3::no_case[x3::lit("Then")];
auto const kwElseIf = x3::no_case[x3::lit("ElseIf")];
auto const kwElse = x3::no_case[x3::lit("Else")];
auto const kwFor = x3::no_case[x3::lit("For")];
auto const kwEach = x3::no_case[x3::lit("Each")];
auto const kwIn = x3::no_case[x3::lit("In")];
auto const kwTo = x3::no_case[x3::lit("To")];
auto const kwStep = x3::no_case[x3::lit("Step")];
auto const kwNext = x3::no_case[x3::lit("Next")];
auto const kwWhile = x3::no_case[x3::lit("While")];
auto const kwWend = x3::no_case[x3::lit("Wend")];
auto const kwDo = x3::no_case[x3::lit("Do")];
auto const kwLoop = x3::no_case[x3::lit("Loop")];
auto const kwUntil = x3::no_case[x3::lit("Until")];
auto const kwSelect = x3::no_case[x3::lit("Select")];
auto const kwCase = x3::no_case[x3::lit("Case")];
auto const kwIs = x3::no_case[x3::lit("Is")];
auto const kwReDim = x3::no_case[x3::lit("ReDim")];
auto const kwPreserve = x3::no_case[x3::lit("Preserve")];
auto const kwWithEvents = x3::no_case[x3::lit("WithEvents")];
auto const kwNew = x3::no_case[x3::lit("New")];
auto const kwExit = x3::no_case[x3::lit("Exit")];
auto const kwCall = x3::no_case[x3::lit("Call")];
auto const kwRaiseEvent = x3::no_case[x3::lit("RaiseEvent")];
auto const kwGoTo = x3::no_case[x3::lit("GoTo")];
auto const kwGoSub = x3::no_case[x3::lit("GoSub")];
auto const kwReturn = x3::no_case[x3::lit("Return")];
auto const kwWith = x3::no_case[x3::lit("With")];
auto const kwAttribute = x3::no_case[x3::lit("Attribute")];
auto const kwTypeOf = x3::no_case[x3::lit("TypeOf")];
/*struct GoTo_table : x3::symbols<vb6_ast::gotoType> {
GoTo_table() {
add("GoTo", vb6_ast::gotoType::goto_v)
("GoSub", vb6_ast::gotoType::gosub_v);
}
} const goto_or_gosub;*/
auto const kwConst = x3::no_case[x3::lit("Const")];
auto const kwDim = x3::no_case[x3::lit("Dim")];
auto const kwType = x3::no_case[x3::lit("Type")];
auto const kwEnum = x3::no_case[x3::lit("Enum")];
auto const kwPublic = x3::no_case[x3::lit("Public")];
auto const kwPrivate = x3::no_case[x3::lit("Private")];
auto const kwGlobal = x3::no_case[x3::lit("Global")];
auto const kwSub = x3::no_case[x3::lit("Sub")];
auto const kwFunction = x3::no_case[x3::lit("Function")];
auto const kwDeclare = x3::no_case[x3::lit("Declare")];
auto const kwAs = x3::no_case[x3::lit("As")];
auto const kwLib = x3::no_case[x3::lit("Lib")];
auto const kwAlias = x3::no_case[x3::lit("Alias")];
auto const kwText = x3::no_case[x3::lit("Text")]; // used in Option Compare and file functions
auto const kwBinary = x3::no_case[x3::lit("Binary")]; // used in Option Compare and file functions
// start of unit
auto const kwOption = x3::no_case[x3::lit("Option")];
auto const kwExplicit = x3::no_case[x3::lit("Explicit")];
auto const kwCompare = x3::no_case[x3::lit("Compare")];
auto const kwBase = x3::no_case[x3::lit("Base")];
auto const kwModule = x3::no_case[x3::lit("Module")];
/* NB:
L'istruzione Option Private Module risulta utile solo per applicazioni host
che supportano il caricamento simultaneo di più progetti e riferimenti
tra progetti caricati. Microsoft Excel, ad esempio, consente di
caricare più progetti e Option Private Module può essere utilizzata per
limitare la visibilità tra progetti. Sebbene Visual Basic consenta di
caricare più progetti, i riferimenti tra progetti non sono mai ammessi.
*/
auto const kwByVal = x3::no_case[x3::lit("ByVal")] >> x3::attr(vb6_ast::param_qualifier::byval);
auto const kwByRef = x3::no_case[x3::lit("ByRef")] >> x3::attr(vb6_ast::param_qualifier::byref);
auto const kwOptional = x3::no_case[x3::lit("Optional")];
}

69
src/vb6_parser_main.cpp Normal file
View File

@@ -0,0 +1,69 @@
//: vb6_parser_main.cpp
// vb6_parser
// Copyright (c) 2022 Federico Aponte
// This code is licensed under GNU Software License (see LICENSE.txt for details)
#include "color_console.hpp"
#include "vb6_parser.hpp" // only for vb6_grammar::getParserInfo()
#include <fstream>
#include <iostream>
#include <iterator>
#include <map>
#include <string_view>
void vb6_test1(std::ostream&);
void vb6_test2(std::ostream&);
void vb6_test_statements(std::ostream&);
void test_vb6_unit(std::ostream&, std::string_view unit);
void test_gosub(std::ostream&);
using namespace std;
void test_vbasic(ostream& os, string const& fname)
{
ifstream is(fname, ios::binary);
if(!is)
{
cerr << "Could not open input file: " << fname << '\n';
return;
}
// no whitespace skipping on the stream
noskipws(is); //is.unsetf(ios::skipws);
string unit;
copy(istream_iterator<char>(is), istream_iterator<char>(),
back_inserter(unit));
test_vb6_unit(os, unit);
}
void test_vbasic(ostream& os)
{
string unit = R"vb(Option Explicit
Dim str As String
Sub foo(a As Integer, b As Long)
)vb";
test_vb6_unit(os, unit);
}
int main()
{
cout << vb6_grammar::getParserInfo() << '\n';
vb6_test1(cout);
vb6_test_statements(cout);
vb6_test2(cout);
test_vbasic(cout, "data/test.bas");
test_vbasic(cout, "data/long_source.bas");
test_vbasic(cout);
//test_gosub(cout);
}

View File

@@ -0,0 +1,75 @@
//: vb6_parser_operators.hpp
// vb6_parser
// Copyright (c) 2022 Federico Aponte
// This code is licensed under GNU Software License (see LICENSE.txt for details)
#pragma once
#include <boost/spirit/home/x3.hpp>
namespace vb6_grammar {
namespace x3 = boost::spirit::x3;
auto const opDot = x3::lit('.');
auto const opExclamation = x3::lit('!');
// arithmetic operators
auto const opExp = x3::lit('^');
auto const opMult = x3::lit('*');
auto const opDiv = x3::lit('/');
auto const opDivint = x3::lit('\\');
auto const opMod = x3::no_case[x3::lit("Mod")];
auto const opPlus = x3::lit('+');
auto const opMinus = x3::lit('-');
auto const opAmp = x3::lit('&');
// relational operators
auto const opLess = x3::lit('<');
auto const opGreater = x3::lit('>');
auto const opLessEqual = x3::lit("<=");
auto const opGreaterEqual = x3::lit(">=");
auto const opEqual = x3::lit('=');
auto const opNotEqual = x3::lit("<>");
// boolean and logical operators
auto const opNot = x3::no_case[x3::lit("Not")];
auto const opAnd = x3::no_case[x3::lit("And")];
auto const opOr = x3::no_case[x3::lit("Or")];
auto const opXor = x3::no_case[x3::lit("Xor")];
auto const opEqv = x3::no_case[x3::lit("Eqv")]; // equivalence: (a IMP b) AND (b IMP a)
auto const opImp = x3::no_case[x3::lit("Imp")]; // material implication: NOT a OR b
auto const opIs = x3::no_case[x3::lit("Is")];
auto const opLike = x3::no_case[x3::lit("Like")];
/*
# Arithmetic Operator Precedence Order
^
- (unary negation)
*, /
\
Mod
+, - (binary addition/subtraction)
&
# Comparison Operator Precedence Order
=
<>
<
>
<=
>=
Like, Is
# Logical Operator Precedence Order
Not
And
Or
Xor
Eqv
Imp
# Source
Sams
Teach Yourself Visual Basic 6 in 24 Hours
Appendix A: Operator Precedence
*/
}

View File

@@ -0,0 +1,50 @@
//: vb6_parser_statements.cpp
// vb6_parser
// Copyright (c) 2022 Federico Aponte
// This code is licensed under GNU Software License (see LICENSE.txt for details)
#include "vb6_config.hpp"
#include "vb6_parser_statements_def.hpp"
namespace vb6_grammar {
/*
template bool parse_rule<iterator_type, context_type>(
????_type rule_
, iterator_type& first, iterator_type const& last
, context_type const& context, ????_type::attribute_type&);
*/
BOOST_SPIRIT_INSTANTIATE(statements::singleStmt_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(statements::statement_block_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(statements::assignmentStmt_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(statements::localvardeclStmt_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(statements::redimStmt_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(statements::exitStmt_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(statements::gotoStmt_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(statements::onerrorStmt_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(statements::resumeStmt_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(statements::labelStmt_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(statements::callimplicitStmt_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(statements::callexplicitStmt_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(statements::raiseeventStmt_type, iterator_type, context_type)
// compound statements
BOOST_SPIRIT_INSTANTIATE(statements::whileStmt_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(statements::doStmt_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(statements::dowhileStmt_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(statements::loopwhileStmt_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(statements::dountilStmt_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(statements::loopuntilStmt_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(statements::forStmt_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(statements::foreachStmt_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(statements::ifelseStmt_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(statements::withStmt_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(statements::selectStmt_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(statements::ifBranch_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(statements::elsifBranch_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(statements::elseBranch_type, iterator_type, context_type)
BOOST_SPIRIT_INSTANTIATE(statements::case_block_type, iterator_type, context_type)
}

View File

@@ -0,0 +1,337 @@
//: vb6_parser_statements_def.hpp
// vb6_parser
// Copyright (c) 2022 Federico Aponte
// This code is licensed under GNU Software License (see LICENSE.txt for details)
#pragma once
#include "vb6_parser.hpp"
#include "vb6_ast_adapt.hpp"
#include "vb6_parser_keywords.hpp"
#include "vb6_parser_operators.hpp"
#include <boost/fusion/include/std_pair.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/utility/annotate_on_success.hpp>
// http://boost.2283326.n4.nabble.com/Horrible-compiletimes-and-memory-usage-while-compiling-a-parser-with-X3-td4689104.html
/*
----
Function return value
VB: The function's name treated as a variable is assigned the return value of
the function. No explicit return statement needed, though it can be used (Exit Function/Property).
C++: Explicit return value needed.
----
Macros
----
Comments
----
Multi-line statements, continuation symbol _
----
On Error GoTo/Resume
----
Exclamation mark operator
----
support ParamArray, Array
----
support AddressOf
----
Implicit type variables
----
*/
namespace vb6_grammar {
namespace x3 = boost::spirit::x3;
// the terminator for every VB6 statement
auto const cmdTermin = x3::eol | ':';
// keyword groups
auto const kwgEndIf = kwEnd > kwIf > cmdTermin;
auto const kwgEndWith = kwEnd > kwWith > cmdTermin;
auto const kwgEndSelect = kwEnd > kwSelect > cmdTermin;
// statements
namespace statements {
#if 0
// FED ???? there seems to be a limit here
// I cannot define this rule with more than 21 alternatives!
auto const singleStmt_def = lonely_comment // critical to have this as the first element
| empty_line
| assignmentStmt
| localvardeclStmt
| redimStmt
| exitStmt
| gotoStmt
| onerrorStmt
//| resumeStmt
| labelStmt
| callimplicitStmt
| callexplicitStmt
| raiseeventStmt
| whileStmt
| doStmt
| dowhileStmt
| loopwhileStmt
| dountilStmt
| loopuntilStmt
| forStmt
//| foreachStmt
| ifelseStmt
//| selectStmt
| withStmt
;
#else
auto const singleStmt1 = assignmentStmt
| localvardeclStmt
| redimStmt
| exitStmt
| gotoStmt
| onerrorStmt
| resumeStmt
| labelStmt
| callimplicitStmt
| callexplicitStmt
| raiseeventStmt
;
auto const singleStmt2 = whileStmt
| doStmt
| dowhileStmt
| loopwhileStmt
| dountilStmt
| loopuntilStmt
| forStmt
| foreachStmt
| ifelseStmt
| selectStmt
| withStmt
;
auto const singleStmt_def = lonely_comment // critical to have this as the first element
| empty_line
| singleStmt1
| singleStmt2;
#endif
auto const statement_block_def = *singleStmt;
auto const assignmentStmt_def = ( (kwSet >> x3::attr(vb6_ast::assignmentType::set))
| (kwLet >> x3::attr(vb6_ast::assignmentType::let))
| (x3::eps >> x3::attr(vb6_ast::assignmentType::na))
) >> decorated_variable >> opEqual >> expression >> cmdTermin;
auto const localvardeclStmt_def = ( kwDim >> x3::attr(vb6_ast::localvardeclType::Dim)
| kwStatic >> x3::attr(vb6_ast::localvardeclType::Static)
)
>> (single_var_declaration % ',')
>> cmdTermin;
// TODO redim statements must be able to act on several variables
auto const redimStmt_def = kwReDim >> -(kwPreserve >> x3::attr(true))
//>> ((decorated_variable > '(' > (expression % ',') > ')') % ',')
>> (decorated_variable > '(' > (expression % ',') > ')')
>> cmdTermin;
auto const exitStmt_def = kwExit > ( kwSub >> x3::attr(vb6_ast::exit_type::sub)
| kwFunction >> x3::attr(vb6_ast::exit_type::function)
| kwProperty >> x3::attr(vb6_ast::exit_type::property)
| kwDo >> x3::attr(vb6_ast::exit_type::do_)
| kwWhile >> x3::attr(vb6_ast::exit_type::while_)
| kwFor >> x3::attr(vb6_ast::exit_type::for_)
)
>> cmdTermin;
auto const gotoLabel = basic_identifier;
auto const gotoStmt_def = ( (kwGoTo >> x3::attr(vb6_ast::gotoType::goto_v))
| (kwGoSub >> x3::attr(vb6_ast::gotoType::gosub_v))
) > gotoLabel >> cmdTermin;
auto const onerrorStmt_def = (kwOn > kwError)
>> ( (kwResume > kwNext >> x3::attr(std::string()) >> x3::attr(vb6_ast::onerror_type::resume_next))
| (kwGoTo >> ('0' >> x3::attr(std::string()) >> x3::attr(vb6_ast::onerror_type::goto_0)))
| (kwGoTo >> ("-1" >> x3::attr(std::string()) >> x3::attr(vb6_ast::onerror_type::goto_neg_1)))
| (kwGoTo >> (gotoLabel >> x3::attr(vb6_ast::onerror_type::goto_label)))
| (kwExit >> (kwSub >> x3::attr(std::string()) >> x3::attr(vb6_ast::onerror_type::exit_sub)))
| (kwExit >> (kwFunction >> x3::attr(std::string()) >> x3::attr(vb6_ast::onerror_type::exit_func)))
| (kwExit >> (kwProperty >> x3::attr(std::string()) >> x3::attr(vb6_ast::onerror_type::exit_property)))
) >> cmdTermin;
auto const resumeStmt_def = kwResume
>> ( (kwNext >> x3::attr(0) >> x3::attr(vb6_ast::resume_type::next))
| ( gotoLabel >> x3::attr(vb6_ast::resume_type::label))
| ( x3::int_ >> x3::attr(vb6_ast::resume_type::line_nr))
| (x3::eps >> x3::attr(0) >> x3::attr(vb6_ast::resume_type::implicit))
)
>> cmdTermin;
auto const labelStmt_def = gotoLabel >> x3::lit(':') >> cmdTermin;
auto const callimplicitStmt_def = sub_identifier >> -(expression % ',') >> x3::attr(false) >> cmdTermin;
auto const callexplicitStmt_def //= kwCall >> functionCall >> x3::attr(true) >> cmdTermin;
//= ( kwCall >> x3::attr()
// | kWRaiseEvent >> x3::attr()
// ) >> sub_identifier
= kwCall >> sub_identifier
>> '(' >> -(expression % ',') >> ')' >> x3::attr(true) >> cmdTermin;
// raising an event is very similar to calling a subroutine
auto const raiseeventStmt_def = kwRaiseEvent >> event_identifier >> '(' >> -(expression % ',') >> ')' >> cmdTermin;
// compound statements
auto const whileStmt_def = kwWhile >> expression >> cmdTermin
>> statement_block
>> kwWend
>> cmdTermin;
// infinite loop
auto const doStmt_def = kwDo >> cmdTermin
>> statement_block
>> kwLoop
>> cmdTermin;
auto const dowhileStmt_def = kwDo >> kwWhile >> expression >> cmdTermin
>> statement_block
>> kwLoop
>> cmdTermin;
auto const loopwhileStmt_def = kwDo >> cmdTermin
>> statement_block
>> kwLoop >> kwWhile >> expression
>> cmdTermin;
auto const dountilStmt_def = kwDo >> kwUntil >> expression >> cmdTermin
>> statement_block
>> kwLoop
>> cmdTermin;
auto const loopuntilStmt_def = kwDo >> cmdTermin
>> statement_block
>> kwLoop >> kwUntil >> expression
>> cmdTermin;
// TODO match the variable after 'Next' with the one of the loop
auto const forStmt_def = kwFor >> decorated_variable >> opEqual
>> expression >> kwTo >> expression
>> -(kwStep >> expression) >> cmdTermin
>> statement_block
>> kwNext >> -x3::omit[decorated_variable]
>> cmdTermin;
// TODO match the variable after 'Next' with the one of the loop
auto const foreachStmt_def = kwFor >> kwEach >> decorated_variable
>> kwIn >> expression >> cmdTermin
>> statement_block
>> kwNext >> -x3::omit[decorated_variable]
>> cmdTermin;
auto const ifBranch_def = x3::rule<class ifBranch, vb6_ast::statements::if_branch>("ifBranch")
= kwIf >> expression
>> kwThen >> cmdTermin
>> statement_block;
auto const elsifBranch_def = x3::rule<class elsifBranch, vb6_ast::statements::if_branch>("elsifBranch")
= kwElseIf >> expression
>> kwThen >> cmdTermin
>> statement_block;
auto const elseBranch_def = x3::rule<class elseBranch, vb6_ast::statements::statement_block>("elseBranch")
= kwElse >> cmdTermin
>> statement_block;
//auto const ifelseifBranches = x3::rule<class ifelseifBranches, std::vector<vb6_ast::if_branch>>()
// = ifBranch >> (*elsifBranch);
auto const ifelseStmt_def = //ifelseifBranches
ifBranch
>> (*elsifBranch)
>> (-elseBranch)
>> kwgEndIf;
// TODO with-statement should also take a function returning a reference to an object
auto const withStmt_def = kwWith >> decorated_variable
//>> -(kwNew >> type_identifier) // FED ????
>> cmdTermin
>> statement_block
>> kwgEndWith;
// 'Is' is legal only when used with the 6 relational operators
// it must appear, alone, on the left side of the operator with
// an expression on the right side
auto const case_relational_expr = kwIs >> ( (opLess >> x3::attr(vb6_ast::rel_operator_type::less))
| (opGreater >> x3::attr(vb6_ast::rel_operator_type::greater))
| (opLessEqual >> x3::attr(vb6_ast::rel_operator_type::less_equal))
| (opGreaterEqual >> x3::attr(vb6_ast::rel_operator_type::greater_equal))
| (opNotEqual >> x3::attr(vb6_ast::rel_operator_type::not_equal))
| (opEqual >> x3::attr(vb6_ast::rel_operator_type::equal))
)
>> expression;
// TODO
/*
Select Case frm.Type
Case 0:
Print 0
Case 1:
Print 1
Case 1 To 4, 7 To 9, 11, 13, Is > MaxNumber
Case Else:
Print "Default"
End Select
*/
auto const case_block_def = kwCase >> expression >> cmdTermin
>> statement_block;
auto const case_block2_def = kwCase >> ( kwElse
| (( (expression >> -(kwTo >> expression))
| case_relational_expr
) % ',')
)
>> cmdTermin
>> statement_block;
// TODO select-statement
auto const selectStmt_def = (kwSelect > kwCase)
>> expression >> cmdTermin
>> (*case_block)
>> kwgEndSelect;
} // namespace statements
BOOST_SPIRIT_DEFINE(
statements::ifBranch
, statements::elsifBranch
, statements::elseBranch
, statements::case_block
)
BOOST_SPIRIT_DEFINE(
statements::singleStmt
, statements::statement_block
, statements::assignmentStmt
, statements::localvardeclStmt
, statements::redimStmt
, statements::exitStmt
, statements::gotoStmt
, statements::onerrorStmt
, statements::resumeStmt
, statements::labelStmt
, statements::callimplicitStmt
, statements::callexplicitStmt
, statements::raiseeventStmt
)
// compound statements
BOOST_SPIRIT_DEFINE(
statements::whileStmt
, statements::doStmt
, statements::dowhileStmt
, statements::loopwhileStmt
, statements::dountilStmt
, statements::loopuntilStmt
, statements::forStmt
, statements::foreachStmt
, statements::ifelseStmt
, statements::withStmt
, statements::selectStmt
)
}

View File

@@ -0,0 +1,508 @@
//: vb6_parser_statements_test.cpp
// vb6_parser
// Copyright (c) 2022 Federico Aponte
// This code is licensed under GNU Software License (see LICENSE.txt for details)
//#define BOOST_SPIRIT_X3_DEBUG
#include "test_grammar_helper.hpp"
#include "vb6_parser.hpp"
#include <gtest/gtest.h>
#include <iostream>
#include <map>
#include <vector>
using namespace std;
namespace x3 = boost::spirit::x3;
GTEST_TEST(vb6_parser_statements, Assignment_0)
{
vb6_ast::statements::assignStmt st;
test_grammar("Set var1 = \" ciao\"\r\n", vb6_grammar::statements::assignmentStmt, st);
EXPECT_EQ(st.type, vb6_ast::assignmentType::set);
EXPECT_EQ(st.var.var, "var1");
EXPECT_FALSE(st.var.ctx.leading_dot);
EXPECT_TRUE(st.var.ctx.elements.empty());
EXPECT_EQ(boost::get<vb6_ast::quoted_string>(
boost::get<vb6_ast::const_expr>(st.rhs.get()).get()
), " ciao");
}
GTEST_TEST(vb6_parser_statements, Assignment_1)
{
vb6_ast::statements::assignStmt st;
test_grammar("Let var2 = 54.7\r\n", vb6_grammar::statements::assignmentStmt, st);
EXPECT_EQ(st.type, vb6_ast::assignmentType::let);
EXPECT_EQ(st.var.var, "var2");
EXPECT_FALSE(st.var.ctx.leading_dot);
EXPECT_TRUE(st.var.ctx.elements.empty());
EXPECT_EQ(boost::get<float>(
boost::get<vb6_ast::const_expr>(st.rhs.get()).get()
), 54.7f);
}
GTEST_TEST(vb6_parser_statements, Assignment_2)
{
vb6_ast::statements::assignStmt st;
test_grammar("var3 = Func(\"descr\", 54.7)\r\n", vb6_grammar::statements::assignmentStmt, st);
EXPECT_EQ(st.type, vb6_ast::assignmentType::na);
EXPECT_EQ(st.var.var, "var3");
EXPECT_FALSE(st.var.ctx.leading_dot);
EXPECT_TRUE(st.var.ctx.elements.empty());
auto& func = boost::get<x3::forward_ast<vb6_ast::func_call>>(
st.rhs.get()
).get();
EXPECT_EQ(func.func_name, "Func");
}
GTEST_TEST(vb6_parser_statements, Assignment_3)
{
vb6_ast::statements::assignStmt st;
test_grammar("str = CStr(i)\r\n", vb6_grammar::statements::assignmentStmt, st);
EXPECT_EQ(st.type, vb6_ast::assignmentType::na);
EXPECT_EQ(st.var.var, "str");
EXPECT_FALSE(st.var.ctx.leading_dot);
EXPECT_TRUE(st.var.ctx.elements.empty());
}
GTEST_TEST(vb6_parser_statements, LocalVarDecl_1)
{
vb6_ast::statements::localVarDeclStmt st;
test_grammar("Dim var As New Form\r\n", vb6_grammar::statements::localvardeclStmt, st);
EXPECT_EQ(st.type, vb6_ast::localvardeclType::Dim);
ASSERT_EQ(st.vars.size(), 1);
EXPECT_TRUE(st.vars[0].construct);
EXPECT_EQ(st.vars[0].name, "var");
ASSERT_TRUE(st.vars[0].type);
EXPECT_EQ(*st.vars[0].type, "Form");
}
GTEST_TEST(vb6_parser_statements, LocalVarDecl_2)
{
vb6_ast::statements::localVarDeclStmt st;
test_grammar("Static var1 As String, var2 As Integer\r\n", vb6_grammar::statements::localvardeclStmt, st);
EXPECT_EQ(st.type, vb6_ast::localvardeclType::Static);
ASSERT_EQ(st.vars.size(), 2);
EXPECT_FALSE(st.vars[0].construct);
EXPECT_EQ(st.vars[0].name, "var1");
ASSERT_TRUE(st.vars[0].type);
EXPECT_EQ(*st.vars[0].type, "String");
EXPECT_FALSE(st.vars[1].construct);
EXPECT_EQ(st.vars[1].name, "var2");
ASSERT_TRUE(st.vars[1].type);
EXPECT_EQ(*st.vars[1].type, "Integer");
}
GTEST_TEST(vb6_parser_statements, ReDim)
{
vb6_ast::statements::redimStmt st;
test_grammar("ReDim var1(15)\r\n", vb6_grammar::statements::redimStmt, st);
//test_grammar("ReDim var1(2*L + 1)\r\n", vb6_grammar::statements::redimStmt, st);
EXPECT_FALSE(st.preserve);
EXPECT_EQ(st.var.var, "var1");
}
GTEST_TEST(vb6_parser_statements, Exit)
{
vb6_ast::statements::exitStmt st;
test_grammar("Exit Function\r\n", vb6_grammar::statements::exitStmt, st);
EXPECT_EQ(st.type, vb6_ast::exit_type::function);
}
GTEST_TEST(vb6_parser_statements, GoTo)
{
vb6_ast::statements::gotoStmt st;
test_grammar("GoSub label1\r\n", vb6_grammar::statements::gotoStmt, st);
EXPECT_EQ(st.type, vb6_ast::gotoType::gosub_v);
EXPECT_EQ(st.label, "label1");
}
GTEST_TEST(vb6_parser_statements, OnError)
{
vb6_ast::statements::onerrorStmt st;
test_grammar("On Error Resume Next\r\n", vb6_grammar::statements::onerrorStmt, st);
EXPECT_EQ(st.type, vb6_ast::onerror_type::resume_next);
EXPECT_TRUE(st.label.empty());
}
GTEST_TEST(vb6_parser_statements, Resume)
{
vb6_ast::statements::resumeStmt st;
test_grammar("Resume\r\n", vb6_grammar::statements::resumeStmt, st);
EXPECT_EQ(st.type, vb6_ast::resume_type::implicit);
test_grammar("Resume Next\r\n", vb6_grammar::statements::resumeStmt, st);
EXPECT_EQ(st.type, vb6_ast::resume_type::next);
test_grammar("Resume 0\r\n", vb6_grammar::statements::resumeStmt, st);
EXPECT_EQ(st.type, vb6_ast::resume_type::line_nr);
EXPECT_EQ(boost::get<int>(st.label_or_line_nr), 0);
test_grammar("Resume resume_point\r\n", vb6_grammar::statements::resumeStmt, st);
EXPECT_EQ(st.type, vb6_ast::resume_type::label);
EXPECT_EQ(boost::get<string>(st.label_or_line_nr), "resume_point");
}
GTEST_TEST(vb6_parser_statements, Label)
{
vb6_ast::statements::labelStmt st;
test_grammar("label1:\r\n", vb6_grammar::statements::labelStmt, st);
EXPECT_EQ(st.label, "label1");
}
GTEST_TEST(vb6_parser_statements, Call_explicit)
{
vb6_ast::statements::callStmt st;
test_grammar("Call Foo(13)\r\n", vb6_grammar::statements::callexplicitStmt, st);
EXPECT_TRUE(st.explicit_call);
EXPECT_EQ(st.sub_name, "Foo");
EXPECT_EQ(st.params.size(), 1);
test_grammar_false("Call Foo 13\r\n", vb6_grammar::statements::callexplicitStmt, st);
}
GTEST_TEST(vb6_parser_statements, Call_implicit)
{
vb6_ast::statements::callStmt st;
test_grammar("Foo \"Sea\", Nothing, False\r\n", vb6_grammar::statements::callimplicitStmt, st);
EXPECT_FALSE(st.explicit_call);
EXPECT_EQ(st.sub_name, "Foo");
EXPECT_EQ(st.params.size(), 3);
}
GTEST_TEST(vb6_parser_statements, RaiseEvent)
{
vb6_ast::statements::raiseeventStmt st;
test_grammar("RaiseEvent OnChange(\"hi!\")\r\n",
vb6_grammar::statements::raiseeventStmt, st);
}
GTEST_TEST(vb6_parser_statements, statement_block)
{
vb6_ast::statements::statement_block st;
test_grammar(
R"vb(Set var1 = " ciao"
Let var2 = 54.7
var3 = Func("descr", 54.7)
str = CStr(i)
Dim var As New Form
Static var1 As String, var2 As Integer
ReDim var1(15)
Exit Function
GoSub label1
On Error Resume Next
Resume Next
label1:
Call Foo(13)
Foo "Sea", Nothing, False
RaiseEvent OnChange("hi!")
)vb", vb6_grammar::statements::statement_block, st);
EXPECT_EQ(st.size(), 15);
EXPECT_EQ(st[0].get().type(), typeid(vb6_ast::statements::assignStmt));
EXPECT_EQ(st[1].get().type(), typeid(vb6_ast::statements::assignStmt));
EXPECT_EQ(st[2].get().type(), typeid(vb6_ast::statements::assignStmt));
EXPECT_EQ(st[3].get().type(), typeid(vb6_ast::statements::assignStmt));
EXPECT_EQ(st[4].get().type(), typeid(vb6_ast::statements::localVarDeclStmt));
EXPECT_EQ(st[5].get().type(), typeid(vb6_ast::statements::localVarDeclStmt));
EXPECT_EQ(st[6].get().type(), typeid(vb6_ast::statements::redimStmt));
EXPECT_EQ(st[7].get().type(), typeid(vb6_ast::statements::exitStmt));
EXPECT_EQ(st[8].get().type(), typeid(vb6_ast::statements::gotoStmt));
EXPECT_EQ(st[9].get().type(), typeid(vb6_ast::statements::onerrorStmt));
EXPECT_EQ(st[10].get().type(), typeid(vb6_ast::statements::resumeStmt));
EXPECT_EQ(st[11].get().type(), typeid(vb6_ast::statements::labelStmt));
EXPECT_EQ(st[12].get().type(), typeid(vb6_ast::statements::callStmt));
EXPECT_EQ(st[13].get().type(), typeid(vb6_ast::statements::callStmt));
EXPECT_EQ(st[14].get().type(), typeid(vb6_ast::statements::raiseeventStmt));
}
GTEST_TEST(vb6_parser_compound_statements, With)
{
vb6_ast::statements::withStmt st;
test_grammar(
R"vb(With obj
'Call Module1.foo(True, .Name)
Call foo(True, .Name)
End With
)vb", vb6_grammar::statements::withStmt, st);
EXPECT_FALSE(st.with_variable.ctx.leading_dot);
EXPECT_TRUE(st.with_variable.ctx.elements.empty());
EXPECT_EQ(st.with_variable.var, "obj");
EXPECT_EQ(st.block.size(), 2);
}
GTEST_TEST(vb6_parser_compound_statements, While)
{
vb6_ast::statements::whileStmt st;
test_grammar(
R"vb(While cond(34, i)
Print str
Wend
)vb",
vb6_grammar::statements::whileStmt, st);
auto& tmp = boost::get<x3::forward_ast<vb6_ast::func_call>>(st.condition.get()).get();
EXPECT_EQ(tmp.func_name, "cond");
EXPECT_EQ(tmp.params.size(), 2);
EXPECT_EQ(st.block.size(), 1);
}
GTEST_TEST(vb6_parser_compound_statements, Do)
{
vb6_ast::statements::doStmt st;
test_grammar(
R"vb(Do
Print str
Loop
)vb",
vb6_grammar::statements::doStmt, st);
EXPECT_EQ(st.block.size(), 1);
}
GTEST_TEST(vb6_parser_compound_statements, DoWhile)
{
vb6_ast::statements::dowhileStmt st;
test_grammar(
R"vb(Do While cond(34, i)
Print str
Loop
)vb",
vb6_grammar::statements::dowhileStmt, st);
auto& tmp = boost::get<x3::forward_ast<vb6_ast::func_call>>(st.condition.get()).get();
EXPECT_EQ(tmp.func_name, "cond");
EXPECT_EQ(tmp.params.size(), 2);
EXPECT_EQ(st.block.size(), 1);
}
GTEST_TEST(vb6_parser_compound_statements, LoopWhile)
{
vb6_ast::statements::loopwhileStmt st;
test_grammar(
R"vb(Do
Print str
Loop While cond(34, i)
)vb",
vb6_grammar::statements::loopwhileStmt, st);
auto& tmp = boost::get<x3::forward_ast<vb6_ast::func_call>>(st.condition.get()).get();
EXPECT_EQ(tmp.func_name, "cond");
EXPECT_EQ(tmp.params.size(), 2);
EXPECT_EQ(st.block.size(), 1);
}
GTEST_TEST(vb6_parser_compound_statements, DoUntil)
{
vb6_ast::statements::dountilStmt st;
test_grammar(
R"vb(Do Until cond(34, i)
Print str
Loop
)vb",
vb6_grammar::statements::dountilStmt, st);
auto& tmp = boost::get<x3::forward_ast<vb6_ast::func_call>>(st.condition.get()).get();
EXPECT_EQ(tmp.func_name, "cond");
EXPECT_EQ(tmp.params.size(), 2);
EXPECT_EQ(st.block.size(), 1);
}
GTEST_TEST(vb6_parser_compound_statements, LoopUntil)
{
vb6_ast::statements::loopuntilStmt st;
test_grammar(
R"vb(Do
Print str
Loop Until cond(34, i)
)vb",
vb6_grammar::statements::loopuntilStmt, st);
auto& tmp = boost::get<x3::forward_ast<vb6_ast::func_call>>(st.condition.get()).get();
EXPECT_EQ(tmp.func_name, "cond");
EXPECT_EQ(tmp.params.size(), 2);
EXPECT_EQ(st.block.size(), 1);
}
GTEST_TEST(vb6_parser_compound_statements, For)
{
vb6_ast::statements::forStmt st;
test_grammar(
R"vb(For i = 1 To 100 Step 2
Dim str As String
str = CStr(i)
Print str
Next i
)vb",
vb6_grammar::statements::forStmt, st);
EXPECT_EQ(st.for_variable.var, "i");
EXPECT_FALSE(st.for_variable.ctx.leading_dot);
EXPECT_TRUE(st.for_variable.ctx.elements.empty());
auto& tmp1 = boost::get<vb6_ast::const_expr>(st.from.get()).get();
auto& tmp2 = boost::get<vb6_ast::const_expr>(st.to.get()).get();
auto& tmp3 = boost::get<vb6_ast::const_expr>(st.step.get()).get();
EXPECT_EQ(boost::get<vb6_ast::integer_dec>(tmp1).val, 1);
EXPECT_EQ(boost::get<vb6_ast::integer_dec>(tmp2).val, 100);
EXPECT_EQ(boost::get<vb6_ast::integer_dec>(tmp3).val, 2);
EXPECT_EQ(st.block.size(), 3);
}
GTEST_TEST(vb6_parser_compound_statements, ForEach)
{
vb6_ast::statements::foreachStmt st;
test_grammar(
R"vb(For Each el In Items
Call Print(el)
Next el
)vb",
vb6_grammar::statements::foreachStmt, st);
EXPECT_EQ(st.for_variable.var, "el");
EXPECT_FALSE(st.for_variable.ctx.leading_dot);
EXPECT_TRUE(st.for_variable.ctx.elements.empty());
auto& tmp1 = boost::get<vb6_ast::decorated_variable>(st.container.get());
EXPECT_TRUE(tmp1.ctx.elements.empty());
EXPECT_EQ(tmp1.var, "Items");
EXPECT_EQ(st.block.size(), 1);
}
GTEST_TEST(vb6_parser_compound_statements, IfElse_substatements)
{
vb6_ast::statements::if_branch ast1;
test_grammar(
R"vb(If cond1 Then
Dim str As String
str = CStr(i)
Print str
)vb",
vb6_grammar::statements::ifBranch, ast1);
auto& tmp1 = boost::get<vb6_ast::decorated_variable>(ast1.condition.get());
EXPECT_EQ(tmp1.var, "cond1");
EXPECT_EQ(ast1.block.size(), 3);
vb6_ast::statements::if_branch ast2;
test_grammar(
R"vb(ElseIf cond2 Then
Dim str As String
str = CStr(i)
Print str
)vb",
vb6_grammar::statements::elsifBranch, ast2);
auto& tmp2 = boost::get<vb6_ast::decorated_variable>(ast2.condition.get());
EXPECT_EQ(tmp2.var, "cond2");
EXPECT_EQ(ast2.block.size(), 3);
vb6_ast::statements::statement_block ast3;
test_grammar(
R"vb(Else
Print str
'End If
)vb",
vb6_grammar::statements::elseBranch, ast3);
EXPECT_EQ(ast3.size(), 2);
}
GTEST_TEST(vb6_parser_compound_statements, IfElse)
{
vb6_ast::statements::ifelseStmt ast;
test_grammar(
R"vb(If cond1 Then
Print "1"
ElseIf cond2 Then
Print "2"
ElseIf cond3 Then
Print "3"
Else
Print "4"
End If
)vb",
vb6_grammar::statements::ifelseStmt, ast);
#ifdef SIMPLE_IF_STATEMENT
EXPECT_EQ(ast.first_branch.block.size(), 5);
auto& tmp1 = boost::get<vb6_ast::decorated_variable>(ast.first_branch.condition.get());
EXPECT_EQ(tmp1.var, "cond1");
#else
EXPECT_EQ(ast.first_branch.block.size(), 1);
auto& tmp0 = boost::get<vb6_ast::decorated_variable>(ast.first_branch.condition.get());
EXPECT_EQ(tmp0.var, "cond1");
ASSERT_EQ(ast.if_branches.size(), 2);
EXPECT_EQ(ast.if_branches[0].block.size(), 1);
auto& tmp1 = boost::get<vb6_ast::decorated_variable>(ast.if_branches[0].condition.get());
EXPECT_EQ(tmp1.var, "cond2");
EXPECT_EQ(ast.if_branches[1].block.size(), 1);
auto& tmp2 = boost::get<vb6_ast::decorated_variable>(ast.if_branches[1].condition.get());
EXPECT_EQ(tmp2.var, "cond3");
#endif
ASSERT_TRUE(ast.else_branch.has_value());
EXPECT_EQ(ast.else_branch.get().size(), 1);
}
GTEST_TEST(vb6_parser_compound_statements, Select)
{
vb6_ast::statements::selectStmt st;
test_grammar(
R"vb(Select Case frm.Type
Case 0
Dim str As String
Call Print(0)
Case 1
' case 1
Dim str As String
'Case 1 To 4, 7 To 9, 11, 13, Is > MaxNumber
' Print "Difficult"
'Case Else
' Print "Default"
End Select
)vb", vb6_grammar::statements::selectStmt, st);
ASSERT_EQ(st.condition.get().type(), typeid(vb6_ast::decorated_variable));
EXPECT_EQ(boost::get<vb6_ast::decorated_variable>(st.condition.get()).var, "Type");
EXPECT_EQ(st.blocks.size(), 2);
ASSERT_EQ(st.blocks[0].case_expr.get().type(), typeid(vb6_ast::const_expr));
EXPECT_EQ(boost::get<vb6_ast::integer_dec>(
boost::get<vb6_ast::const_expr>(st.blocks[0].case_expr.get()).get()
).val, 0);
ASSERT_EQ(st.blocks[0].block.size(), 2);
EXPECT_EQ(st.blocks[0].block[0].get().type(), typeid(vb6_ast::statements::localVarDeclStmt));
EXPECT_EQ(st.blocks[0].block[1].get().type(), typeid(vb6_ast::statements::callStmt));
ASSERT_EQ(st.blocks[1].case_expr.get().type(), typeid(vb6_ast::const_expr));
EXPECT_EQ(boost::get<vb6_ast::integer_dec>(
boost::get<vb6_ast::const_expr>(st.blocks[1].case_expr.get()).get()
).val, 1);
ASSERT_EQ(st.blocks[1].block.size(), 6);
EXPECT_EQ(st.blocks[1].block[0].get().type(), typeid(vb6_ast::lonely_comment));
EXPECT_EQ(st.blocks[1].block[1].get().type(), typeid(vb6_ast::statements::localVarDeclStmt));
}

813
src/vb6_parser_test.cpp Normal file
View File

@@ -0,0 +1,813 @@
//: vb6_parser_test.cpp
// vb6_parser
// Copyright (c) 2022 Federico Aponte
// This code is licensed under GNU Software License (see LICENSE.txt for details)
//#define BOOST_SPIRIT_X3_DEBUG
#include "test_grammar_helper.hpp"
#include "vb6_parser.hpp"
#include "vb6_ast_printer.hpp"
#include <boost/optional/optional_io.hpp>
#include <gtest/gtest.h>
#include <iostream>
#include <map>
#include <vector>
using namespace std;
namespace x3 = boost::spirit::x3;
GTEST_TEST(vb6_parser_simple, lonely_comment)
{
vector<vb6_ast::lonely_comment> ast;
test_grammar(
"' This is comment line 1\r\n' Comment line 2\r\n",
*vb6_grammar::lonely_comment, ast);
ASSERT_EQ(ast.size(), 2);
EXPECT_EQ(ast[0].content, " This is comment line 1");
EXPECT_EQ(ast[1].content, " Comment line 2");
}
GTEST_TEST(vb6_parser_simple, empty_lines)
{
vector<
boost::variant<vb6_ast::empty_line, vb6_ast::lonely_comment>
> ast;
// critical to have lonely_comment first in the parsing rule
auto const G = *(vb6_grammar::lonely_comment | vb6_grammar::empty_line);
auto str = "' comment1\r\n"
"\r\n"
"' comment2\r\n";
test_grammar(str, G, ast);
ASSERT_EQ(ast.size(), 3);
EXPECT_EQ(boost::get<vb6_ast::lonely_comment>(ast[0]).content, " comment1");
EXPECT_NO_THROW(boost::get<vb6_ast::empty_line>(ast[1]));
EXPECT_EQ(boost::get<vb6_ast::lonely_comment>(ast[2]).content, " comment2");
}
GTEST_TEST(vb6_parser_simple, quoted_string)
{
string str;
test_grammar("\"Quoted string.\"", vb6_grammar::quoted_string, str);
EXPECT_EQ(str, "Quoted string.");
}
GTEST_TEST(vb6_parser_simple, basic_identifier)
{
string id;
test_grammar("iden_tifier", vb6_grammar::basic_identifier, id);
EXPECT_EQ(id, "iden_tifier");
}
GTEST_TEST(vb6_parser_simple, var_identifier)
{
string var;
test_grammar("g_logger", vb6_grammar::var_identifier, var);
EXPECT_EQ(var, "g_logger");
var.clear();
test_grammar("forth ", vb6_grammar::sub_identifier, var);
EXPECT_EQ(var, "forth");
var.clear();
test_grammar("subroutine ", vb6_grammar::sub_identifier, var);
EXPECT_EQ(var, "subroutine");
/*
string code = "sub ";
string_view code_sv = code;
auto it1 = cbegin(code_sv);
auto const it2 = cend(code_sv);
ASSERT_FALSE(boost::spirit::x3::phrase_parse(it1, it2, vb6_grammar::sub_identifier, vb6_grammar::skip, var));
EXPECT_EQ(it1, begin(code_sv));
*/
}
GTEST_TEST(vb6_parser_simple, type_identifier)
{
string type;
test_grammar("Long", vb6_grammar::type_identifier, type);
EXPECT_EQ(type, "Long");
}
GTEST_TEST(vb6_parser_simple, complex_type_identifier)
{
string type;
test_grammar("VB.Form", vb6_grammar::complex_type_identifier, type);
EXPECT_EQ(type, "VB.Form");
}
GTEST_TEST(vb6_parser_simple, const_expression_non_numeric)
{
vb6_ast::const_expr ast;
string str;
str = "\"una stringa\""s;
test_grammar(str, vb6_grammar::const_expression, ast);
EXPECT_EQ(boost::get<vb6_ast::quoted_string>(ast.get()), "una stringa");
str = "True"s;
test_grammar(str, vb6_grammar::const_expression, ast);
EXPECT_EQ(boost::get<bool>(ast.get()), true);
str = "Nothing"s;
test_grammar(str, vb6_grammar::const_expression, ast);
EXPECT_NO_THROW(boost::get<vb6_ast::nothing>(ast.get()));
}
GTEST_TEST(vb6_parser_simple, const_expression_integers)
{
vb6_ast::const_expr ast;
string str;
str = "1234%"s;
test_grammar(str, vb6_grammar::const_expression, ast);
EXPECT_EQ(boost::get<vb6_ast::integer_dec>(ast.get()).val, 1234);
str = "1234&"s;
test_grammar(str, vb6_grammar::const_expression, ast);
EXPECT_EQ(boost::get<vb6_ast::long_dec>(ast.get()).val, 1234);
str = "&Hcafedead&"s;
test_grammar(str, vb6_grammar::const_expression, ast);
EXPECT_EQ(boost::get<vb6_ast::long_hex>(ast.get()).val, 0xcafedead);
str = "&01234&"s;
test_grammar(str, vb6_grammar::const_expression, ast);
EXPECT_EQ(boost::get<vb6_ast::long_oct>(ast.get()).val, 01234);
str = "1234"s;
test_grammar(str, vb6_grammar::const_expression, ast);
EXPECT_EQ(boost::get<vb6_ast::integer_dec>(ast.get()).val, 1234);
}
GTEST_TEST(vb6_parser_simple, const_expression_floats)
{
vb6_ast::const_expr ast;
string str;
str = "1234!"s;
test_grammar(str, vb6_grammar::const_expression, ast);
EXPECT_EQ(boost::get<float>(ast.get()), 1234.0f);
str = "1234#"s;
test_grammar(str, vb6_grammar::const_expression, ast);
EXPECT_EQ(boost::get<double>(ast.get()), 1234.0);
str = "2.8"s;
test_grammar(str, vb6_grammar::const_expression, ast);
EXPECT_EQ(boost::get<float>(ast.get()), 2.8f);
}
GTEST_TEST(vb6_parser_simple, sample_expression)
{
vb6_ast::expression ast;
test_grammar("foo1(foo2(3, M.x_coord), True)", vb6_grammar::expression, ast);
EXPECT_EQ(ast.get().type(), typeid(x3::forward_ast<vb6_ast::func_call>));
EXPECT_EQ(boost::get<x3::forward_ast<vb6_ast::func_call>>(ast.get()).get().func_name, "foo1");
}
GTEST_TEST(vb6_parser_simple, single_var_declaration)
{
vb6_ast::variable P1;
test_grammar("g_logger As Long", vb6_grammar::single_var_declaration, P1);
EXPECT_EQ(P1.name, "g_logger");
EXPECT_FALSE(P1.construct);
EXPECT_TRUE(P1.type);
if(P1.type)
{
EXPECT_EQ(*P1.type, "Long");
}
vb6_ast::variable P2;
test_grammar("name As New String", vb6_grammar::single_var_declaration, P2);
EXPECT_EQ(P2.name, "name");
EXPECT_TRUE(P2.construct);
EXPECT_TRUE(P2.type);
if(P2.type)
{
EXPECT_EQ(*P2.type, "String");
}
}
GTEST_TEST(vb6_parser_tests, record_declaration)
{
vb6_ast::record rec;
test_grammar("Type PatRecord\r\n name As String\r\n age As Integer\r\nEnd Type\r\n",
vb6_grammar::record_declaration, rec);
EXPECT_EQ(rec.name, "PatRecord");
EXPECT_EQ(rec.at, vb6_ast::access_type::na);
ASSERT_EQ(rec.members.size(), 2);
EXPECT_EQ(rec.members[0].name, "name");
EXPECT_TRUE(rec.members[0].type);
if(rec.members[0].type)
{
EXPECT_EQ(*rec.members[0].type, "String");
}
EXPECT_FALSE(rec.members[0].construct);
EXPECT_EQ(rec.members[1].name, "age");
EXPECT_TRUE(rec.members[1].type);
if(rec.members[1].type)
{
EXPECT_EQ(*rec.members[1].type, "Integer");
}
EXPECT_FALSE(rec.members[1].construct);
}
GTEST_TEST(vb6_parser_tests, enum_declaration)
{
vb6_ast::vb_enum enum1;
test_grammar("Enum PatTypes\r\n inpatient\r\n outpatient\r\nEnd Enum\r\n",
vb6_grammar::enum_declaration, enum1);
EXPECT_EQ(enum1.name, "PatTypes");
EXPECT_EQ(enum1.at, vb6_ast::access_type::na);
ASSERT_EQ(enum1.values.size(), 2);
EXPECT_EQ(enum1.values[0].first, "inpatient");
EXPECT_FALSE(enum1.values[0].second);
EXPECT_EQ(enum1.values[1].first, "outpatient");
EXPECT_FALSE(enum1.values[1].second);
}
GTEST_TEST(vb6_parser_tests, global_var_declaration)
{
auto str = "Global g_logger As Long, v1, XRes As New Object, ptr As Module.MyRec, g_active As Boolean\r\n"s;
vb6_ast::global_var_decls vars;
test_grammar(str, vb6_grammar::global_var_declaration, vars);
EXPECT_EQ(vars.at, vb6_ast::access_type::global);
EXPECT_FALSE(vars.with_events);
ASSERT_EQ(vars.vars.size(), 5);
EXPECT_EQ(vars.vars[0].name, "g_logger");
EXPECT_FALSE(vars.vars[0].construct);
EXPECT_TRUE(vars.vars[0].type);
if(vars.vars[0].type)
{
EXPECT_EQ(*vars.vars[0].type, "Long");
}
EXPECT_EQ(vars.vars[1].name, "v1");
EXPECT_FALSE(vars.vars[1].construct);
EXPECT_FALSE(vars.vars[1].type);
EXPECT_EQ(vars.vars[2].name, "XRes");
EXPECT_TRUE(vars.vars[2].construct);
EXPECT_TRUE(vars.vars[2].type);
if(vars.vars[2].type)
{
EXPECT_EQ(*vars.vars[2].type, "Object");
}
EXPECT_EQ(vars.vars[3].name, "ptr");
EXPECT_FALSE(vars.vars[3].construct);
EXPECT_TRUE(vars.vars[3].type);
if(vars.vars[3].type)
{
EXPECT_EQ(*vars.vars[3].type, "MyRec");
}
EXPECT_EQ(vars.vars[4].name, "g_active");
EXPECT_FALSE(vars.vars[4].construct);
EXPECT_TRUE(vars.vars[4].type);
if(vars.vars[4].type)
{
EXPECT_EQ(*vars.vars[4].type, "Boolean");
}
}
GTEST_TEST(vb6_parser_tests, const_var_declaration1)
{
auto cstr = "Const e As Single = 2.8, pi As Double = 3.14, u As Integer = -1\r\n"s;
vb6_ast::const_var_stat cvars;
test_grammar(cstr, vb6_grammar::const_var_declaration, cvars);
ASSERT_EQ(cvars.size(), 3);
EXPECT_EQ(cvars[0].var.name, "e");
if(cvars[0].var.type)
{
EXPECT_EQ(*cvars[0].var.type, "Single");
}
EXPECT_FALSE(cvars[0].var.construct);
EXPECT_EQ(boost::get<float>(cvars[0].value.get()), 2.8f);
EXPECT_EQ(cvars[1].var.name, "pi");
if(cvars[1].var.type)
{
EXPECT_EQ(*cvars[1].var.type, "Double");
}
EXPECT_FALSE(cvars[1].var.construct);
EXPECT_EQ(boost::get<float>(cvars[1].value.get()), 3.14f);
EXPECT_EQ(cvars[2].var.name, "u");
if(cvars[2].var.type)
{
EXPECT_EQ(*cvars[2].var.type, "Integer");
}
EXPECT_FALSE(cvars[2].var.construct);
EXPECT_EQ(boost::get<vb6_ast::integer_dec>(cvars[2].value.get()).val, -1);
}
GTEST_TEST(vb6_parser_tests, const_var_declaration2)
{
vb6_ast::const_var_stat cvars;
test_grammar("Private Const PI As Double = 3.1415\r\n",
vb6_grammar::const_var_declaration, cvars);
ASSERT_EQ(cvars.size(), 1);
EXPECT_EQ(cvars[0].var.name, "PI");
if(cvars[0].var.type)
{
EXPECT_EQ(*cvars[0].var.type, "Double");
}
EXPECT_FALSE(cvars[0].var.construct);
EXPECT_EQ(boost::get<float>(cvars[0].value.get()), 3.1415f);
}
GTEST_TEST(vb6_parser_tests, param_decl)
{
vb6_ast::func_param fp;
test_grammar("Optional ByVal name As String = \"pippo\"", vb6_grammar::param_decl, fp);
EXPECT_TRUE(fp.isoptional);
EXPECT_TRUE(fp.qualifier);
if(fp.qualifier)
{
EXPECT_EQ(*fp.qualifier, vb6_ast::param_qualifier::byval);
}
EXPECT_EQ(fp.var.name, "name");
EXPECT_FALSE(fp.var.construct);
EXPECT_TRUE(fp.var.type);
if(fp.var.type)
{
EXPECT_EQ(*fp.var.type, "String");
}
EXPECT_TRUE(fp.defvalue);
if(fp.defvalue)
{
EXPECT_EQ(boost::get<vb6_ast::quoted_string>(fp.defvalue.get()), "pippo");
}
}
GTEST_TEST(vb6_parser_tests, param_list_decl)
{
vector<vb6_ast::func_param> fps;
test_grammar("ByVal name As String, ByRef val As Integer",
-(vb6_grammar::param_decl % ','), fps);
ASSERT_EQ(fps.size(), 2);
EXPECT_FALSE(fps[0].isoptional);
EXPECT_TRUE(fps[0].qualifier);
if(fps[0].qualifier)
{
EXPECT_EQ(*fps[0].qualifier, vb6_ast::param_qualifier::byval);
}
EXPECT_EQ(fps[0].var.name, "name");
EXPECT_FALSE(fps[0].var.construct);
EXPECT_TRUE(fps[0].var.type);
if(fps[0].var.type)
{
EXPECT_EQ(*fps[0].var.type, "String");
}
EXPECT_FALSE(fps[0].defvalue);
EXPECT_FALSE(fps[1].isoptional);
EXPECT_TRUE(fps[1].qualifier);
if(fps[1].qualifier)
{
EXPECT_EQ(*fps[1].qualifier, vb6_ast::param_qualifier::byref);
}
EXPECT_EQ(fps[1].var.name, "val");
EXPECT_FALSE(fps[1].var.construct);
EXPECT_TRUE(fps[1].var.type);
if(fps[1].var.type)
{
EXPECT_EQ(*fps[1].var.type, "Integer");
}
EXPECT_FALSE(fps[1].defvalue);
}
GTEST_TEST(vb6_parser_tests, event_declaration)
{
vb6_ast::eventHead event_decl;
test_grammar("Public Event OnChange(ByVal Text As String)\r\n",
vb6_grammar::eventHead, event_decl);
EXPECT_EQ(event_decl.at, vb6_ast::access_type::public_);
EXPECT_EQ(event_decl.name, "OnChange");
EXPECT_EQ(event_decl.params.size(), 1);
}
GTEST_TEST(vb6_parser_tests, function_head)
{
vb6_ast::functionHead fh;
test_grammar(
"Private Function OneFunc(ByVal name As String, ByRef val As Integer) As Integer\r\n",
vb6_grammar::functionHead, fh);
EXPECT_EQ(fh.at, vb6_ast::access_type::private_);
EXPECT_EQ(fh.name, "OneFunc");
EXPECT_EQ(fh.params.size(), 2);
ASSERT_TRUE(fh.return_type);
EXPECT_EQ(*fh.return_type, "Integer");
}
GTEST_TEST(vb6_parser_tests, function_head_no_params)
{
vb6_ast::functionHead fh;
test_grammar(
"Private Function NoParamFunc() As Object\r\n", vb6_grammar::functionHead, fh);
EXPECT_EQ(fh.at, vb6_ast::access_type::private_);
EXPECT_EQ(fh.name, "NoParamFunc");
EXPECT_TRUE(fh.params.empty());
ASSERT_TRUE(fh.return_type);
EXPECT_EQ(*fh.return_type, "Object");
}
GTEST_TEST(vb6_parser_tests, subroutine_head)
{
vb6_ast::subHead sh;
test_grammar(
"Private Sub my_sub(ByRef str As String, ByVal valid As Boolean)\r\n",
vb6_grammar::subHead, sh);
EXPECT_EQ(sh.at, vb6_ast::access_type::private_);
EXPECT_EQ(sh.name, "my_sub");
EXPECT_EQ(sh.params.size(), 2);
}
GTEST_TEST(vb6_parser_tests, subroutine_head2)
{
auto str = "Private Sub my_sub(ByRef str As String, ByVal valid As Boolean, Optional ByVal flag As Boolean = True)\r\n"s;
vb6_ast::subHead sh;
test_grammar(str, vb6_grammar::subHead, sh);
EXPECT_EQ(sh.at, vb6_ast::access_type::private_);
EXPECT_EQ(sh.name, "my_sub");
EXPECT_EQ(sh.params.size(), 3);
}
GTEST_TEST(vb6_parser_tests, subroutine_head_with_optional_params)
{
vb6_ast::subHead sh;
test_grammar(
"Private Sub my_sub(ByRef str As String, Optional ByVal valid As Boolean = false)\r\n",
vb6_grammar::subHead, sh);
EXPECT_EQ(sh.at, vb6_ast::access_type::private_);
EXPECT_EQ(sh.name, "my_sub");
ASSERT_EQ(sh.params.size(), 2);
EXPECT_TRUE(sh.params[0].qualifier.has_value());
if (sh.params[0].qualifier)
EXPECT_EQ(sh.params[0].qualifier.get(), vb6_ast::param_qualifier::byref);
EXPECT_EQ(sh.params[0].var.name, "str");
if(sh.params[0].var.type)
{
EXPECT_EQ(*sh.params[0].var.type, "String");
}
EXPECT_FALSE(sh.params[0].var.construct);
EXPECT_FALSE(sh.params[0].isoptional);
EXPECT_FALSE(sh.params[0].defvalue);
EXPECT_TRUE(sh.params[1].qualifier);
if(sh.params[1].qualifier)
{
EXPECT_EQ(sh.params[1].qualifier.get(), vb6_ast::param_qualifier::byval);
}
EXPECT_EQ(sh.params[1].var.name, "valid");
if(sh.params[1].var.type)
{
EXPECT_EQ(*sh.params[1].var.type, "Boolean");
}
EXPECT_FALSE(sh.params[1].var.construct);
EXPECT_TRUE(sh.params[1].isoptional);
EXPECT_TRUE(sh.params[1].defvalue);
if(sh.params[0].defvalue)
{
EXPECT_EQ(boost::get<bool>(*sh.params[0].defvalue), false);
}
}
GTEST_TEST(vb6_parser_tests, property_let_head)
{
auto str = "Public Property Let Width(ByVal w As Integer)\r\n"s;
vb6_ast::propertyLetHead ast;
test_grammar(str, vb6_grammar::property_letHead, ast);
EXPECT_EQ(ast.at, vb6_ast::access_type::public_);
EXPECT_EQ(ast.name, "Width");
EXPECT_EQ(ast.params.size(), 1);
}
GTEST_TEST(vb6_parser_tests, property_get_head)
{
auto str = "Public Property Get Width() As Integer\r\n"s;
vb6_ast::propertyGetHead ast;
test_grammar(str, vb6_grammar::property_getHead, ast);
EXPECT_EQ(ast.at, vb6_ast::access_type::public_);
EXPECT_EQ(ast.name, "Width");
EXPECT_EQ(ast.params.size(), 0);
ASSERT_TRUE(ast.return_type);
EXPECT_EQ(*ast.return_type, "Integer");
}
GTEST_TEST(vb6_parser_tests, dll_subroutine_declaration)
{
vb6_ast::externalSub extsub;
auto str = "Private Declare Sub BeepVB Lib \"kernel32.dll\" Alias \"Beep\" (ByVal time As Long, ByVal xx As Single)\r\n"s;
test_grammar(str, vb6_grammar::external_sub_decl, extsub);
EXPECT_EQ(extsub.at, vb6_ast::access_type::private_);
EXPECT_EQ(extsub.name, "BeepVB");
EXPECT_EQ(extsub.alias, "Beep");
EXPECT_EQ(extsub.params.size(), 2);
EXPECT_EQ(extsub.lib, "kernel32.dll");
}
GTEST_TEST(vb6_parser_tests, dll_function_declaration)
{
vb6_ast::externalFunction extfunc;
auto str = "Private Declare Function BeepVB Lib \"kernel32.dll\" Alias \"Beep\" (ByVal time As Long, ByVal xx As Single) As Long\r\n"s;
test_grammar(str, vb6_grammar::external_function_decl, extfunc);
EXPECT_EQ(extfunc.at, vb6_ast::access_type::private_);
EXPECT_EQ(extfunc.name, "BeepVB");
EXPECT_TRUE(extfunc.return_type);
if(extfunc.return_type)
{
EXPECT_EQ(*extfunc.return_type, "Long");
}
EXPECT_EQ(extfunc.alias, "Beep");
EXPECT_EQ(extfunc.params.size(), 2);
EXPECT_EQ(extfunc.lib, "kernel32.dll");
}
GTEST_TEST(vb6_parser_tests, identifier_context)
{
vb6_ast::identifier_context ctx;
test_grammar("var1.func().pnt1.", vb6_grammar::identifier_context, ctx);
EXPECT_FALSE(ctx.leading_dot);
ASSERT_EQ(ctx.elements.size(), 3);
EXPECT_EQ(boost::get<string>(ctx.elements[0].get()), "var1");
auto& tmp = boost::get<x3::forward_ast<vb6_ast::func_call>>(ctx.elements[1].get()).get();
EXPECT_EQ(tmp.func_name, "func");
EXPECT_TRUE(tmp.params.empty());
EXPECT_EQ(boost::get<string>(ctx.elements[2].get()), "pnt1");
}
GTEST_TEST(vb6_parser_tests, decorated_variable)
{
vb6_ast::decorated_variable dec_var;
test_grammar("var1.func().pnt1.X", vb6_grammar::decorated_variable, dec_var);
EXPECT_FALSE(dec_var.ctx.leading_dot);
ASSERT_EQ(dec_var.ctx.elements.size(), 3);
EXPECT_EQ(boost::get<string>(dec_var.ctx.elements[0].get()), "var1");
auto& tmp = boost::get<x3::forward_ast<vb6_ast::func_call>>(dec_var.ctx.elements[1].get()).get();
EXPECT_EQ(tmp.func_name, "func");
EXPECT_TRUE(tmp.params.empty());
EXPECT_EQ(boost::get<string>(dec_var.ctx.elements[2].get()), "pnt1");
EXPECT_EQ(dec_var.var, "X");
}
GTEST_TEST(vb6_parser_tests, attribute_block)
{
vb6_ast::STRICT_MODULE_STRUCTURE::module_attributes attrs;
auto str = "Attribute ModuleName = \"MyForm\"\r\n"
"Attribute ProgID = \"00-00-00-00\"\r\n"s;
test_grammar(str, *vb6_grammar::attributeDef, attrs);
EXPECT_EQ(attrs.size(), 2);
auto it1 = attrs.find("ModuleName");
EXPECT_NE(it1, attrs.cend());
if(it1 != attrs.cend())
{
EXPECT_EQ(it1->second, "MyForm");
}
auto it2 = attrs.find("ProgID");
EXPECT_NE(it2, attrs.cend());
if(it2 != attrs.cend())
{
EXPECT_EQ(it2->second, "00-00-00-00");
}
}
GTEST_TEST(vb6_parser_tests, attributes)
{
vb6_ast::STRICT_MODULE_STRUCTURE::module_attributes ast;
auto str = R"vb(Attribute ModuleName = "MyForm"
Attribute ProgID = "00-00-00-00"
)vb"s;
//test_grammar(str, vb6_grammar::preamble, ast);
test_grammar(str, *vb6_grammar::attributeDef, ast);
EXPECT_EQ(ast.size(), 2);
auto const it1 = ast.find("ModuleName");
EXPECT_NE(it1, ast.cend());
if(it1 != ast.cend())
{
EXPECT_EQ(it1->second, "MyForm");
}
auto const it2 = ast.find("ProgID");
EXPECT_NE(it2, ast.cend());
if(it2 != ast.cend())
{
EXPECT_EQ(it2->second, "00-00-00-00");
}
}
GTEST_TEST(vb6_parser_tests, options)
{
vector<vb6_ast::module_option> ast;
auto str = R"vb(Option Explicit
Option Base 0
)vb"s;
test_grammar(str, *vb6_grammar::option_item, ast);
ASSERT_EQ(ast.size(), 2);
EXPECT_EQ(ast[0], vb6_ast::module_option::explicit_);
EXPECT_EQ(ast[1], vb6_ast::module_option::base_0);
}
#if 0
GTEST_TEST(vb6_parser_tests, declaration_block)
{
auto const declarations
= boost::spirit::x3::rule<class _, vector<vb6_ast::STRICT_MODULE_STRUCTURE::declaration>>("declaration_block")
= *vb6_grammar::STRICT_MODULE_STRUCTURE::declaration;
vector<vb6_ast::STRICT_MODULE_STRUCTURE::declaration> decls;
auto str = "Const e As Double = 2.8, pi As Double = 3.14, u As Integer = -1\r\n"
"Global g_logger As Long, v1, XRes As Object, ptr As MyRec, g_active As Boolean\r\n"
"Private Declare Sub PFoo Lib \"mylib.dll\" Alias \"PFoo\" (ByVal val As Long)\r\n"
"Enum MyEnum1\r\n c1 = 0\r\n c2 = 1\r\nEnd Enum\r\n"
"Public Type MyRecord1\r\n v1 As String\r\n v2 As Long\r\nEnd Type\r\n"s;
test_grammar(str, declarations, decls);
ASSERT_EQ(decls.size(), 5);
EXPECT_NO_THROW(boost::get<vb6_ast::const_var_stat>(decls[0].get()));
EXPECT_NO_THROW(boost::get<vb6_ast::global_var_decls>(decls[1].get()));
EXPECT_NO_THROW(boost::get<vb6_ast::externalSub>(decls[2].get()));
EXPECT_NO_THROW(boost::get<vb6_ast::vb_enum>(decls[3].get()));
EXPECT_NO_THROW(boost::get<vb6_ast::record>(decls[4].get()));
}
GTEST_TEST(vb6_parser_tests, bas_unit_STRICT_MODULE_STRUCTURE)
{
vb6_ast::STRICT_MODULE_STRUCTURE::vb_module ast;
auto str = R"vb(Attribute ModuleName = "MyForm"
Attribute ProgID = "00-00-00-00"
Option Explicit
Option Base 0
' declarations
Const u As Integer = 1234
Global g_logger As Long
Enum MyEnum1
c1 = 0
c2 = 1
End Enum
Sub my_sub(ByRef str As String)
End Sub
Function my_fun(ByRef str As String) As Long
End Function
)vb"s;
test_grammar(str, vb6_grammar::STRICT_MODULE_STRUCTURE::basModDef, ast);
vb6_ast_printer P(cout);
P(ast);
EXPECT_EQ(ast.attrs.size(), 2);
auto const it1 = ast.attrs.find("ModuleName");
EXPECT_NE(it1, ast.attrs.cend());
if(it1 != ast.attrs.cend())
{
EXPECT_EQ(it1->second, "MyForm");
}
auto const it2 = ast.attrs.find("ProgID");
EXPECT_NE(it2, ast.attrs.cend());
if(it2 != ast.attrs.cend())
{
EXPECT_EQ(it2->second, "00-00-00-00");
}
EXPECT_EQ(ast.opts.items.size(), 4);
if(ast.opts.items.size() == 4)
{
#if 0
EXPECT_EQ(ast.opts.items[0], vb6_ast::module_option::explicit_);
EXPECT_EQ(ast.opts.items[1], vb6_ast::module_option::base_0);
#else
EXPECT_EQ(boost::get<vb6_ast::module_option>(ast.opts.items[0].get()), vb6_ast::module_option::explicit_);
EXPECT_EQ(boost::get<vb6_ast::module_option>(ast.opts.items[1].get()), vb6_ast::module_option::base_0);
#endif
EXPECT_NO_THROW(boost::get<vb6_ast::empty_line>(ast.opts.items[2].get()));
EXPECT_NO_THROW(boost::get<vb6_ast::lonely_comment>(ast.opts.items[3].get()));
}
EXPECT_EQ(ast.declarations.size(), 3);
if(ast.declarations.size() == 3)
{
EXPECT_NO_THROW(boost::get<vb6_ast::const_var_stat>(ast.declarations[0].get()));
EXPECT_NO_THROW(boost::get<vb6_ast::global_var_decls>(ast.declarations[1].get()));
EXPECT_NO_THROW(boost::get<vb6_ast::vb_enum>(ast.declarations[2].get()));
}
EXPECT_EQ(ast.functions.size(), 2);
if(ast.functions.size() == 2)
{
EXPECT_NO_THROW(boost::get<vb6_ast::subDef>(ast.functions[0].get()));
EXPECT_NO_THROW(boost::get<vb6_ast::functionDef>(ast.functions[1].get()));
}
}
#endif
GTEST_TEST(vb6_parser_tests, bas_unit)
{
vb6_ast::vb_module ast;
auto str = R"vb(Attribute ModuleName = "MyForm"
Attribute ProgID = "00-00-00-00"
Option Explicit
Option Base 0
' declarations
Const u As Integer = 1234
Global g_logger As Long
Enum MyEnum1
c1 = 0
c2 = 1
End Enum
Sub my_sub(ByRef str As String)
End Sub
Function my_fun(ByRef str As String) As Long
End Function
)vb"s;
test_grammar(str, vb6_grammar::basModDef, ast);
vb6_ast_printer P(cout);
P(ast);
ASSERT_EQ(ast.size(), 11);
EXPECT_EQ(boost::get<vb6_ast::module_attribute>(ast[0].get()).first, "ModuleName");
EXPECT_EQ(boost::get<vb6_ast::module_attribute>(ast[1].get()).first, "ProgID");
EXPECT_EQ(boost::get<vb6_ast::module_option>(ast[2].get()), vb6_ast::module_option::explicit_);
EXPECT_EQ(boost::get<vb6_ast::module_option>(ast[3].get()), vb6_ast::module_option::base_0);
EXPECT_NO_THROW(boost::get<vb6_ast::empty_line>(ast[4].get()));
EXPECT_NO_THROW(boost::get<vb6_ast::lonely_comment>(ast[5].get()));
EXPECT_NO_THROW(boost::get<vb6_ast::const_var_stat>(boost::get<vb6_ast::declaration>(ast[6].get())));
EXPECT_NO_THROW(boost::get<vb6_ast::global_var_decls>(boost::get<vb6_ast::declaration>(ast[7].get())));
EXPECT_NO_THROW(boost::get<vb6_ast::vb_enum>(boost::get<vb6_ast::declaration>(ast[8].get())));
EXPECT_NO_THROW(boost::get<vb6_ast::subDef>(ast[9].get()));
EXPECT_NO_THROW(boost::get<vb6_ast::functionDef>(ast[10].get()));
}
GTEST_TEST(vb6_parser_tests, trailing_comment)
{
auto str = "Global g_var As Long ' how can we catch a trailing comment?\r\n"s;
vb6_ast::global_var_decls vars;
test_grammar(str, vb6_grammar::global_var_declaration, vars);
EXPECT_EQ(vars.at, vb6_ast::access_type::global);
EXPECT_FALSE(vars.with_events);
ASSERT_EQ(vars.vars.size(), 1);
EXPECT_EQ(vars.vars[0].name, "g_var");
EXPECT_FALSE(vars.vars[0].construct);
EXPECT_TRUE(vars.vars[0].type);
if(vars.vars[0].type)
{
EXPECT_EQ(*vars.vars[0].type, "Long");
}
}

View File

@@ -0,0 +1,29 @@
//: vb6_parser_test_main.cpp
// vb6_parser
// Copyright (c) 2022 Federico Aponte
// This code is licensed under GNU Software License (see LICENSE.txt for details)
#include <gtest/gtest.h>
#include <iostream>
int main(int argc, char* argv[])
{
testing::InitGoogleTest(&argc, argv);
// --gtest_filter = Test_Cases1*
// --gtest_repeat = 1000
// --gtest_break_on_failure
// --gtest_shuffle
// --gtest_random_seed=SEED
// --gtest_color=no
// --gtest_print_time=0
// --gtest_print_utf8=0
// --gtest_output=xml:path_to_output_file
// --gtest_output=json:path_to_output_file
//::test::GTEST_FLAG(list_tests) = true;
//::testing::GTEST_FLAG(filter) = "*empty_lines*";
return RUN_ALL_TESTS();
}

200
src/vb6_test1.cpp Normal file
View File

@@ -0,0 +1,200 @@
//: vb6_test1.cpp
// vb6_parser
// Copyright (c) 2022 Federico Aponte
// This code is licensed under GNU Software License (see LICENSE.txt for details)
//#define BOOST_SPIRIT_X3_DEBUG
#include "test_grammar_helper.hpp"
#include "vb6_parser.hpp"
#include "vb6_ast_printer.hpp"
#include <iostream>
#include <map>
#include <vector>
using namespace std;
//namespace x3 = boost::spirit::x3;
void test_vb6_unit(ostream& os, std::string_view unit)
{
os << "---- " << __func__ << " ----\n";
#if 0
vb6_ast::STRICT_MODULE_STRUCTURE::vb_module ast;
auto const G = vb6_grammar::STRICT_MODULE_STRUCTURE::basModDef;
#else
vb6_ast::vb_module ast;
auto const G = vb6_grammar::basModDef;
#endif
test_grammar(os, "test_vb6_unit", G, unit, ast);
vb6_ast_printer P(os);
P(ast);
}
void vb6_test1(ostream& os)
{
string var;
test_grammar(os, "var_identifier", vb6_grammar::var_identifier, "g_logger", var);
os << var << '\n';
string type;
test_grammar(os, "type_identifier", vb6_grammar::type_identifier, "Long", type);
os << type << '\n';
vb6_ast_printer Print(os);
vb6_ast::expression expr;
test_grammar(os, "expression", vb6_grammar::expression, "foo1(foo2(3, M.x_coord), True)", expr);
Print(expr);
os << '\n';
vb6_ast::variable P1;
test_grammar(os, "single_var_declaration", vb6_grammar::single_var_declaration, "g_logger As Long", P1);
Print(P1);
os << '\n';
vb6_ast::variable res1;
test_grammar(os, "single_var_declaration", vb6_grammar::single_var_declaration, "name As String", res1);
Print(res1);
os << '\n';
vb6_ast::record rec1;
test_grammar(os, "record_declaration", vb6_grammar::record_declaration,
"Type PatRecord\r\n name As String\r\n age As Integer\r\nEnd Type\r\n", rec1);
Print(rec1);
vb6_ast::vb_enum enum1;
test_grammar(os, "enum_declaration", vb6_grammar::enum_declaration,
"Enum PatTypes\r\n inpatient\r\n outpatient\r\nEnd Enum\r\n", enum1);
Print(enum1);
auto str = "Global g_logger As Long, v1, XRes As New Object, ptr As Module.MyRec, g_active As Boolean\r\n"s;
vb6_ast::global_var_decls vars;
test_grammar(os, "global_var_declaration", vb6_grammar::global_var_declaration, str, vars);
Print(vars);
auto cstr = "Const bActivate As Boolean = False"
", keyword As String = \"Module\""
", e As Single = 2.8"
", pi As Double = 3.14"
", u As Integer = -1\r\n"s;
vb6_ast::const_var_stat cvars;
test_grammar(os, "const_var_declaration", vb6_grammar::const_var_declaration, cstr, cvars);
Print(cvars);
vb6_ast::const_var_stat cvar;
test_grammar(os, "const_var_declaration", vb6_grammar::const_var_declaration,
"Private Const PI As Double = 3.1415\r\n", cvar);
Print(cvar);
vb6_ast::func_param res2;
test_grammar(os, "param_decl", vb6_grammar::param_decl, "Optional ByVal name As String = \"pippo\"", res2);
Print(res2);
os << '\n';
vector<vb6_ast::func_param> res3;
test_grammar(os, "param_list_decl", -(vb6_grammar::param_decl % ','),
"ByVal name As String, ByRef val As Integer", res3);
for(auto& el : res3)
{
Print(el);
os << ", ";
}
os << '\n';
vb6_ast::eventHead event_decl;
test_grammar(os, "event declaration", vb6_grammar::eventHead,
"Public Event OnChange(ByVal Text As String)\r\n", event_decl);
Print(event_decl);
vb6_ast::functionHead func_header1;
test_grammar(os, "function head", vb6_grammar::functionHead,
"Private Function OneFunc(ByVal name As String, ByRef val As Integer) As Integer\r\n", func_header1);
Print(func_header1);
vb6_ast::functionHead func_header2;
test_grammar(os, "function head (no params)", vb6_grammar::functionHead,
"Private Function NoParamFunc() As Object\r\n", func_header2);
Print(func_header2);
vb6_ast::subHead sub_header1;
test_grammar(os, "subroutine_head", vb6_grammar::subHead,
"Private Sub my_sub(ByRef str As String, ByVal valid As Boolean)\r\n", sub_header1);
Print(sub_header1);
auto str2 = "Private Sub my_sub(ByRef str As String, ByVal valid As Boolean, Optional ByVal flag As Boolean = True)\r\n"s;
vb6_ast::subHead sh;
test_grammar(os, "subroutine_head2", vb6_grammar::subHead, str2, sh);
Print(sh);
vb6_ast::subHead sub_header2;
test_grammar(os, "subroutine head with optional params", vb6_grammar::subHead,
"Private Sub my_sub(ByRef str As String, Optional ByVal valid As Boolean = false)\r\n", sub_header2);
Print(sub_header2);
vector<vb6_ast::lonely_comment> comments;
test_grammar(os, "lonely_comment", *vb6_grammar::lonely_comment,
"' This is comment line 1\r\n' Comment line 2\r\n", comments);
for(auto& el : comments)
Print(el);
vb6_ast::subDef sub1;
test_grammar(os, "subroutine definition 1", vb6_grammar::subDef,
"Private Sub my_sub(ByRef str As String, ByVal valid As Boolean)\r\nEnd Sub\r\n", sub1);
Print(sub1);
vb6_ast::subDef sub2;
test_grammar(os, "subroutine definition 2", vb6_grammar::subDef,
R"vb(Public Sub my_sub(ByRef p1 As String, ByVal p2 As Boolean, p3 As Long)
' this is a comment
Dim x As Long, y As String, frm As New VB.Form
Set x = Module1.GetField("size").Value
y = "word"
'On Error GoTo err_handler
Dim z As Variant
'ReDim arr(256, 256)
Call routine1()
Call routine2(34, "ciao")
Call routine3(1.5, Module1.GetField("name1").Value)
'Call subroutine3(1.5, Value)
routine1
routine2 34, "ciao"
routine3 1.5, Module1.GetField("name1").Value
Exit Sub
err_handler:
GoTo final
final:
End Sub
)vb", sub2);
Print(sub2);
//x3::error_handler<std::string::const_iterator> err_hndlr(it1, it2, out, "dummy.cpp");
// we pass our error handler to the parser so we can access
// it later on in our on_error and on_sucess handlers
//auto const parser = x3::with<x3::error_handler_tag>(std::ref(err_hndlr))[vb6_grammar::functionDef];
vb6_ast::functionDef func1;
test_grammar(os, "function definition", vb6_grammar::functionDef,
R"vb(Private Function foo(ByRef p1 As String, ByVal p2 As Boolean, p3 As Long) As Long
Static s_State As String
Set x = "sss"
foo = 5
End Function
)vb", func1);
Print(func1);
vb6_ast::functionDef func_def1;
test_grammar(os, "function definition (no params)", vb6_grammar::functionDef,
"Private Function NoParamFunc() As Object\r\nNoParamFunc = Nothing\r\nEnd Function\r\n", func_def1);
Print(func_def1);
vb6_ast::functionDef func2;
test_grammar(os, "function definition (1 line)", vb6_grammar::functionDef,
"Function foo(str As String) As String: foo = \"ciao\": End Function\r\n", func2);
Print(func2);
}

259
src/vb6_test2.cpp Normal file
View File

@@ -0,0 +1,259 @@
//: vb6_test2.cpp
// vb6_parser
// Copyright (c) 2022 Federico Aponte
// This code is licensed under GNU Software License (see LICENSE.txt for details)
//#define BOOST_SPIRIT_X3_DEBUG
#include "test_grammar_helper.hpp"
#include "vb6_parser.hpp"
#include "vb6_ast_printer.hpp"
#include <iostream>
#include <map>
#include <vector>
using namespace std;
//namespace x3 = boost::spirit::x3;
void vb6_test_statements(ostream& os)
{
vb6_ast_printer Print(os);
vb6_ast::statements::assignStmt P2;
test_grammar(os, "assignmentStmt 0", vb6_grammar::statements::assignmentStmt,
"Set var1 = \" ciao\"\r\n", P2);
Print(P2);
vb6_ast::statements::assignStmt P3;
test_grammar(os, "assignmentStmt 1", vb6_grammar::statements::assignmentStmt,
"Let var2 = 54.7\r\n", P3);
Print(P3);
vb6_ast::statements::assignStmt P4;
test_grammar(os, "assignmentStmt 2", vb6_grammar::statements::assignmentStmt,
"var3 = Func(\"descr\", 54.7)\r\n", P4);
Print(P4);
vb6_ast::statements::assignStmt P5;
test_grammar(os, "assignmentStmt 3", vb6_grammar::statements::assignmentStmt,
"str = CStr(i)\r\n", P5);
Print(P5);
vb6_ast::statements::localVarDeclStmt localVarDecl;
test_grammar(os, "localvardeclStmt", vb6_grammar::statements::localvardeclStmt,
"Dim var1 As String, var2 As Integer\r\n", localVarDecl);
Print(localVarDecl);
vb6_ast::statements::redimStmt redim;
test_grammar(os, "redimStmt", vb6_grammar::statements::redimStmt,
"ReDim var1(15)\r\n", redim);
Print(redim);
vb6_ast::statements::exitStmt exitSt;
test_grammar(os, "exitStmt", vb6_grammar::statements::exitStmt,
"Exit Function\r\n", exitSt);
Print(exitSt);
vb6_ast::statements::gotoStmt gotoSt;
test_grammar(os, "gotoStmt", vb6_grammar::statements::gotoStmt,
"GoSub label1\r\n", gotoSt);
Print(gotoSt);
vb6_ast::statements::onerrorStmt onerrorSt;
test_grammar(os, "onerrorStmt", vb6_grammar::statements::onerrorStmt,
"On Error Resume Next\r\n", onerrorSt);
Print(onerrorSt);
vb6_ast::statements::labelStmt labelSt;
test_grammar(os, "labelStmt", vb6_grammar::statements::labelStmt,
"label1:\r\n", labelSt);
Print(labelSt);
vb6_ast::statements::callStmt callSt1;
test_grammar(os, "callexplicitStmt", vb6_grammar::statements::callexplicitStmt,
"Call Foo(13)\r\n", callSt1);
Print(callSt1);
vb6_ast::statements::callStmt callSt2;
test_grammar(os, "callimplicitStmt", vb6_grammar::statements::callimplicitStmt,
"Foo \"Sea\", Nothing, False\r\n", callSt2);
Print(callSt2);
vb6_ast::statements::raiseeventStmt raise_ev;
test_grammar(os, "raise_event_statement", vb6_grammar::statements::raiseeventStmt,
"RaiseEvent OnChange(\"hi!\")\r\n", raise_ev);
vb6_ast::statements::withStmt with_stat;
test_grammar(os, "with_statement", vb6_grammar::statements::withStmt,
R"vb(With obj
'Call Module1.foo(True, .Name)
Call foo(True, .Name)
End With
)vb", with_stat);
Print(with_stat);
vb6_ast::statements::whileStmt while_stat;
test_grammar(os, "while_statement", vb6_grammar::statements::whileStmt,
R"vb(While cond(34, i)
Print str
Wend
)vb", while_stat);
Print(while_stat);
vb6_ast::statements::dowhileStmt dowhile_stat;
test_grammar(os, "dowhile_statement", vb6_grammar::statements::dowhileStmt,
R"vb(Do While cond(34, i)
Print str
Loop
)vb", dowhile_stat);
Print(dowhile_stat);
vb6_ast::statements::loopwhileStmt loopwhile_st;
test_grammar(os, "loopwhile_statement", vb6_grammar::statements::loopwhileStmt,
R"vb(Do
Print str
Loop While cond(34, i)
)vb", loopwhile_st);
Print(loopwhile_st);
vb6_ast::statements::dountilStmt dountil_st;
test_grammar(os, "dountil_statement", vb6_grammar::statements::dountilStmt,
R"vb(Do Until cond(34, i)
Print str
Loop
)vb", dountil_st);
Print(dountil_st);
vb6_ast::statements::loopuntilStmt loopuntil_st;
test_grammar(os, "loopuntil_statement", vb6_grammar::statements::loopuntilStmt,
R"vb(Do
Print str
Loop Until cond(34, i)
)vb", loopuntil_st);
Print(loopuntil_st);
vb6_ast::statements::forStmt forloop_stat;
test_grammar(os, "for_statement", vb6_grammar::statements::forStmt,
R"vb(For i = 1 To 100 Step 2
Dim str As String
str = CStr(i)
Print str
Next i
)vb", forloop_stat);
Print(forloop_stat);
vb6_ast::statements::foreachStmt foreach_stat;
test_grammar(os, "foreach_statement", vb6_grammar::statements::foreachStmt,
R"vb(For Each el In Items
Print el
Next el
)vb", foreach_stat);
Print(foreach_stat);
std::vector<vb6_ast::statements::if_branch> branches;
//vb6_ast::if_branch branches;
test_grammar(os, "elseif_branch", *vb6_grammar::statements::elsifBranch,
R"vb(ElseIf func2(Nothing) Then
proc2a "elseif branch", 2
ElseIf func3(Nothing) Then
proc2b "elseif branch", 2
proc2c "elseif branch", 2
)vb", branches);
for(auto& el : branches)
{
os << "ElseIf ";
Print(el.condition);
os << " Then\n";
// statements
for(auto& st : el.block)
Print(st);
}
vb6_ast::statements::ifelseStmt ifelse_stat1;
test_grammar(os, "ifelse_statement", vb6_grammar::statements::ifelseStmt,
R"vb(If func1("name", True, 45.8, -45) Then
proc1 "if branch", 1
ElseIf func2(Nothing) Then
proc2 "elseif branch", 2
Else
proc3 "else branch", 3
End If
)vb", ifelse_stat1);
Print(ifelse_stat1);
vb6_ast::statements::selectStmt select_stat;
test_grammar(os, "select_statement", vb6_grammar::statements::selectStmt,
R"vb(Select Case frm.Type
Case 0
Dim str As String
Call Print(0)
Case 1
' case 1
Dim str As String
'Case 1 To 4, 7 To 9, 11, 13, Is > MaxNumber
' Print "Difficult"
'Case Else
' Print "Default"
End Select
)vb", select_stat);
Print(select_stat);
}
void vb6_test2(ostream& os)
{
vb6_ast_printer Print(os);
vb6_ast::record rec;
auto str3 = "Private Type myrec\r\n name As String\r\n age As Integer\r\nEnd Type\r\n"s;
test_grammar(os, "record_declaration", vb6_grammar::record_declaration, str3, rec);
Print(rec);
vb6_ast::vb_enum enum_res;
auto str4 = "Private Enum ComprKind\r\n NoCompr = 0\r\n Jpeg = 1\r\n Tiff\r\nEnd Enum\r\n"s;
test_grammar(os, "enum_declaration", vb6_grammar::enum_declaration, str4, enum_res);
Print(enum_res);
vb6_ast::externalSub extsub;
auto str5 = "Private Declare Sub Beep Lib \"kernel32.dll\" Alias \"Beep\" (ByVal time As Long, ByVal xx As Single)\r\n"s;
test_grammar(os, "DLL subroutine declaration", vb6_grammar::external_sub_decl, str5, extsub);
Print(extsub);
vb6_ast::externalFunction extfunc;
auto str6 = "Private Declare Function Beep Lib \"kernel32.dll\" Alias \"Beep\" (ByVal time As Long, ByVal xx As Single) As Long\r\n"s;
test_grammar(os, "DLL function declaration", vb6_grammar::external_function_decl, str6, extfunc);
Print(extfunc);
auto const declarations //= x3::rule<class _, vector<vb6_ast::STRICT_MODULE_STRUCTURE::declaration>>()
= *vb6_grammar::STRICT_MODULE_STRUCTURE::declaration;
#if 0
vector<vb6_ast::STRICT_MODULE_STRUCTURE::declaration> decls;
auto str7 = "Const e As Double = 2.8, pi As Double = 3.14, u As Integer = -1\r\n"
"Global g_logger As Long, v1, XRes As Object, ptr As MyRec, g_active As Boolean\r\n"
"Private Declare Sub PFoo Lib \"mylib.dll\" Alias \"PFoo\" (ByVal val As Long)\r\n"
"Enum MyEnum1\r\n c1 = 0\r\n c2 = 1\r\nEnd Enum\r\n"
"Public Type MyRecord1\r\n v1 As String\r\n v2 As Long\r\nEnd Type\r\n"s;
test_grammar(os, "Declaration block", declarations, str7, decls);
os << str7 << '\n';
for(auto& el : decls)
Print(el);
os << '\n';
#endif
vb6_ast::identifier_context ctx;
test_grammar(os, "identifier_context", vb6_grammar::identifier_context, "var1.func().pnt1.", ctx);
Print(ctx);
os << '\n';
vb6_ast::decorated_variable dec_var;
test_grammar(os, "decorated_variable", vb6_grammar::decorated_variable, "var1.func().pnt1.X", dec_var);
Print(dec_var);
os << '\n';
// Dim obj As VB.Form
// pnt.x = 4.5
// Call Module1.func1(45)
}

364
src/visual_basic_x3.hpp Normal file
View File

@@ -0,0 +1,364 @@
//: visual_basic_x3.hpp
// vb6_parser
// Copyright (c) 2022 Federico Aponte
// This code is licensed under GNU Software License (see LICENSE.txt for details)
#pragma once
#include "color_console.hpp"
#include "vb6_ast.hpp"
#include "vb6_ast_adapt.hpp"
#include "vb6_parser.hpp"
#include <boost/fusion/include/std_pair.hpp>
#include <boost/spirit/home/x3.hpp>
#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>
/*
http://stackoverflow.com/questions/2366097/what-does-putting-an-exclamation-point-in-front-of-an-object-reference-varia
https://support.microsoft.com/en-us/kb/129287
http://bytecomb.com/the-bang-exclamation-operator-in-vba/
*/
/*
http://ciere.com/cppnow15/x3_docs/
http://ciere.com/cppnow15/
Parser Directory Structure
* fun
* ast.hpp
* ast_adapted.hpp
* common.hpp
* expression.hpp
* expression_def.hpp
* src
* expression.cpp
* test
*/
/*
PARSING
http://stackoverflow.com/questions/3455456/what-kinds-of-patterns-could-i-enforce-on-the-code-to-make-it-easier-to-translat/3460977#3460977
http://www.semanticdesigns.com/Products/DMS/LifeAfterParsing.html
http://www.semanticdesigns.com/Products/DMS/DMSToolkit.html
https://support.microsoft.com/en-us/kb/216388
http://www.tangiblesoftwaresolutions.com/Product_Details/VB_to_CPlusPlus_Converter_Details.html
https://sourceforge.net/projects/vbtocconv/
http://www.vbto.net/
https://ubuntuforums.org/showthread.php?t=1590707
http://edndoc.esri.com/arcobjects/9.0/ArcGISDevHelp/DevelopmentEnvs/Cpp/ArcGIS%20Development/sampleconversions.htm
http://ezbasic.sourceforge.net/
https://github.com/antlr/grammars-v4/blob/master/vb6/VisualBasic6.g4
http://slebok.github.io/zoo/index.html
MIX
http://powerbasic.com/index.php
http://www.codeproject.com/Articles/710181/Visual-Basic-A-giant-more-powerful-than-ever
https://www.indiegogo.com/projects/a-replacement-to-visual-basic-6-ide-and-compiler#/
http://www.ioprogrammo.it/index.php?topic=20151.0;wap2
http://vb.mvps.org/
http://www.semanticdesigns.com/Products/Services/VB6Migration.html?Home=LegacyMigration
http://betselection.cc/resources/vb6-replacements/
https://blog.xojo.com/2013/06/19/a-modern-alternative-to-visual-basic/
http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html
https://www.indiegogo.com/projects/a-replacement-to-visual-basic-6-ide-and-compiler#/
http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/7462243-provide-a-visual-basic-6-community-edition-to-al
FRX
http://www.vbforums.com/showthread.php?221901-frx-file-format
http://planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=5539&lngWId=1
https://forum.powerbasic.com/forum/user-to-user-discussions/special-interest-groups/vb-conversion/59957-extract-files-from-vb6-frx-discussion
https://forum.powerbasic.com/forum/user-to-user-discussions/source-code/59945-extract-files-from-visual-basic-vb5-vb6-frx-form-resource-files
putref_ -> Set
put_ -> Let
get_ -> Get
*/
/*
== Exclamation Point (!) Operator ==
Use the ! operator only on a class or interface as a dictionary access operator.
The class or interface must have a default property that accepts a single String argument.
The identifier immediately following the ! operator becomes the string argument to the default property.
*/
/*
== Implicit types ==
* the type depends on a suffix
* there are 6 suffixes: $ % & @ ! #
* variables with a suffix cannot be explicitly declared (e.g. Dim name$ As String is forbiddden)
* £ is a valid character for names just like a-z and _ (aaargh)
my_str$ = "ciao" ' String
my_short% = 34 ' Integer
my_long& = 65537 ' Long
my_float! = 3.1 ' Single
my_double# = 1.23 ' Double
my_currency@ = 23.56 ' Currency
Debug.Print "my_str$ -> "; TypeName(my_str$) ' String
Debug.Print "my_short% -> "; TypeName(my_short%) ' Integer
Debug.Print "my_long& -> "; TypeName(my_long&) ' Long
Debug.Print "my_float! -> "; TypeName(my_float!) ' Single
Debug.Print "my_double# -> "; TypeName(my_double#) ' Double
Debug.Print "my_currency@ -> "; TypeName(my_currency@) ' Currency
*/
/*
http://www.vb6.us/tutorials/error-handling
On [Local] Error { GoTo [ line | 0 | -1 ] | Resume Next }
-> "On Local Error" is a hold over from previous versions of VB
GoTo line Enables the error-handling routine that starts at the line
specified in the required line argument. The line argument is any
line label or line number. If a run-time error occurs, control
branches to the specified line, making the error handler active.
The specified line must be in the same procedure as the On Error
statement, or a compile-time error will occur.
This form of the On Error statement redirects program execution
to the line label specified. When you use this form of On Error,
a block of error handling code is constructed following the label.
GoTo 0 Disables enabled error handler in the current procedure and
resets it to Nothing.
GoTo -1 Disables enabled exception in the current procedure and resets it
to Nothing.
Resume Next Specifies that when a run-time error occurs, control goes to the
statement immediately following the statement where the error
occurred, and execution continues from that point. Use this form
rather than On Error GoTo when accessing objects.
This form of the On Error statement tells VB to continue with the
line of code following the line where the error occurred. With
this type of error trap, you would normally test for an error at
selected points in the program code where you anticipate that an
error may occur.
Private Sub cmd1_Click()
On Error GoTo ErrHandler
Dim val As Long
val = CLng("we34")
Debug.Print val
ErrHandler:
Debug.Print "cmd1_Click -> " & VBA.Information.Err.Number & " - " & VBA.Information.Err.Description
End Sub
Private Sub cmd2_Click()
On Error Resume Next
Dim val As Long
val = CLng("we34") ' fails with type mismatch
Debug.Print val
' this succeeds, but the error stays because the operation above failed
val = CLng("12")
Debug.Print "cmd2_Click -> " & VBA.Information.Err.Number & " - " & VBA.Information.Err.Description
End Sub
Private Sub cmd3_Click()
' the error is apperently cleared at the beginning of each sub or function
Debug.Print "cmd3_Click -> " & VBA.Information.Err.Number & " - " & VBA.Information.Err.Description
End Sub
*/
/*
http://stackoverflow.com/questions/9290731/what-is-the-effect-of-a-semicolon-at-the-end-of-a-line
http://www.vb6.us/tutorials/understanding-semicolans-and-print-method
--> Print is more of a pseudo-method that the compiler handles directly.
This is why it does not work within a With-block, etc.
Print is a fundamental BASIC statement that dates back to the first days of the
language in the mid-1960s. Print is used to display lines of data on a form,
picture box, printer, and the immediate (Debug) window; it can also be used to
write records of data to a file. In VB, Print is implemented as a method.
The general format for the Print method is:
[object.]Print [expressionlist]
where object refers to one of the objects mentioned above (Form, PictureBox,
Debug window, Printer) and expressionlist refers to a list of one or more
numeric or string expressions to print.
Items in the expression list may be separated with semicolons (;) or commas (,).
A semicolon or comma in the expression list determines where the next output begins:
; (semicolon) means print immediately after the last value.
, (comma) means print at the start of the next "print zone".
Items in the expression list of a Print statement that are separated by
semicolons print immediately after one another. In the statement
Print "Hello,"; strName; "How are you today?"
*/
/*
vbrun100.Dll -> msvbvm50.dll -> msvbvm60.dll
*/
struct vb6_keywords_ : boost::spirit::x3::symbols<unsigned>
{
vb6_keywords_()
{
add("Option", 0) ("Explicit", 0) ("Compare", 0) ("Module", 0) ("Open", 0) ("Close", 0) ("Put", 0)
("Access", 0) ("Line", 0) ("Read", 0) ("Print", 0) ("Write", 0) ("Input", 0) ("Output", 0) ("Random", 0) ("Append", 0) ("Text", 0)
("Binary", 0) ("Lock", 0) ("Shared", 0) ("Tab", 0) ("On", 0) ("Error", 0) ("Resume", 0) ("Const", 0) ("True", 0) ("False", 0)
// data types
("Boolean", 0) ("Byte", 0) ("Integer", 0) ("Long", 0) ("Single", 0) ("Double", 0)
("Currency", 0) ("Date", 0) ("String", 0) ("Variant", 0) ("Object", 0) ("Any", 0)
("Event", 0) ("Sub", 0) ("Function", 0) ("Propery", 0) ("Get", 0) ("Let", 0)
("Set", 0) ("Declare", 0) ("As", 0) ("Lib", 0) ("Alias", 0) ("Begin", 0) ("End", 0) ("ByVal", 0)
("ByRef", 0) ("ParamArray", 0) ("Optional", 0) /*("Default", 0)*/ ("AddressOf", 0) ("Static", 0)
("Public", 0) ("Private", 0) ("Global", 0) ("Friend", 0) ("If", 0) ("Then", 0) ("ElseIf", 0) ("Else", 0)
("For", 0) ("Each", 0) ("In", 0) ("To", 0) ("Step", 0) ("Next", 0)
("Debug.Assert", 0) ("Debug.Print", 0) ("Stop", 0)
("While", 0) ("Wend", 0) ("Do", 0) ("Loop", 0) ("Until", 0) ("Select", 0) ("Case", 0) ("Type", 0)
("Enum", 0) ("Dim", 0) ("ReDim", 0) ("Preserve", 0) ("Erase", 0) ("WithEvents", 0) ("Implements", 0)
("New", 0) ("Exit", 0) ("GoTo", 0) ("Call", 0) ("Rem", 0) ("Like", 0)
("GoSub", 0) ("RaiseEvent", 0) ("With", 0) ("Nothing", 0) ("Attribute", 0) ("TypeOf", 0)
("Not", 0) ("And", 0) ("Or", 0) ("Xor", 0) ("Is", 0) ("Mod", 0) ("Imp", 0)
;
}
};
//inline vb6_keywords_ vb6_keywords;
/*
normally, the syntax is
Open "filename.txt" for MODE [Lock] as #FILENO
whereas MODE is one of Input, Output, Random, Append, Binary (Random is default)
if you don't specify Lock, it normally shouldn't lock except on modes output and append.
don't forget to Close #FILENO after operation.
you might want to take a look at http://www.profsr.com/vb/vbless08.htm
*/
/*
Attribute VB_Name = "clsBulge"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Attribute VB_Description = "Some text here"
Attribute VB_Ext_KEY = "SavedWithClassBuilder" ,"Yes"
Attribute VB_Ext_KEY = "Member0" ,"collBulges"
Attribute VB_Ext_KEY = "Top_Level" ,"Yes"
' Header
Attribute VB_Name = "ClassOrModuleName"
Attribute VB_GlobalNameSpace = False ' ignored
Attribute VB_Creatable = False ' ignored
Attribute VB_PredeclaredId = False ' a Value of True creates a default global instance
Attribute VB_Exposed = True ' Controls how the class can be instanced.
' Module Scoped Variables
Attribute variableName.VB_VarUserMemId = 0 ' Zero indicates that this is the default member of the class.
Attribute variableName.VB_VarDescription = "some string" ' Adds the text to the Object Browser information for this variable.
' Procedures
Attribute procName.VB_Description = "some string" ' Adds the text to the Object Browser information for the procedure.
Attribute procName.VB_UserMemId = someInteger
' 0: Makes the function the default member of the class.
' -4: Specifies that the function returns an Enumerator.
https://christopherjmcclellan.wordpress.com/2015/04/21/vb-attributes-what-are-they-and-why-should-we-use-them/
https://stackoverflow.com/questions/33648764/what-does-the-attribute-keyword-do-in-vb6
*/
namespace vb6_grammar {
namespace x3 = boost::spirit::x3;
/*
Sub ComboLoad(ByVal ctrl As VB.Control, ByVal D As String, ByVal active As Boolean, ByVal CodeVal As String)
Function CodeText(ByVal s As String) As String
Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Declare Sub xxx Lib "yyy" Alias "zzz" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any)
Preprocessor!!!!
#Const x = 6
#If x = 5 Then
...
#ElseIf
...
#End If
*/
/*
== built-in functions, classes, constants ==
Open Close Put Get - For Binary Random Append Read Write - As Binary Text
== libraries (modules) embedded in the language ==
VBA.Information -> Err VarType VarName
VBA.Conversion -> Val Str CStr CInt CLong, Error
VBA.Interaction -> CreateObject IIf MsgBox
VBA.Strings -> StrConv Split Join InStr LCase UCase Len LenB Left$ Right$ Mid$ Space$
VBA.FileSystem -> FreeFile Kill
VBA._HiddenModule -> VarPtr, StrPtr, ObjPtr
VB -> Load App
== built-in constants ==
VbStrConv -> vbUnicode
*/
// not really keywords, these are subroutines, functions and constants that
// are built into the language
// file handling
auto const kwOpen = x3::rule<class kwOpen, std::string>("kwOpen")
= x3::no_case[x3::lit("Open")];
auto const kwClose = x3::rule<class kwClose, std::string>("kwClose")
= x3::no_case[x3::lit("Close")];
auto const kwPut = x3::rule<class kwPut, std::string>("kwPut")
= x3::no_case[x3::lit("Put")];
auto const kwAccess = x3::rule<class kwAccess, std::string>("kwAccess")
= x3::no_case[x3::lit("Access")];
auto const kwLine = x3::rule<class kwLine, std::string>("kwLine")
= x3::no_case[x3::lit("Line")];
auto const kwRead = x3::rule<class kwRead, std::string>("kwRead")
= x3::no_case[x3::lit("Read")];
auto const kwWrite = x3::rule<class kwWrite, std::string>("kwWrite")
= x3::no_case[x3::lit("Write")];
auto const kwInput = x3::rule<class kwInput, std::string>("kwInput")
= x3::no_case[x3::lit("Input")];
auto const kwOutput = x3::rule<class kwOutput, std::string>("kwOutput")
= x3::no_case[x3::lit("Output")];
auto const kwRandom = x3::rule<class kwRandom, std::string>("kwRandom")
= x3::no_case[x3::lit("Random")];
auto const kwAppend = x3::rule<class kwAppend, std::string>("kwAppend")
= x3::no_case[x3::lit("Append")];
auto const kwLock = x3::rule<class kwLock, std::string>("kwLock")
= x3::no_case[x3::lit("Lock")];
auto const kwShared = x3::rule<class kwShared, std::string>("kwShared")
= x3::no_case[x3::lit("Shared")];
auto const kwSeek = x3::rule<class kwSeek, std::string>("kwSeek")
= x3::no_case[x3::lit("Seek")];
auto const kwPrint = x3::rule<class kwPrint, std::string>("kwPrint")
= x3::no_case[x3::lit("Print")];
auto const kwTab = x3::rule<class kwTab, std::string>("kwTab")
= x3::no_case[x3::lit("Tab")];
auto const kwDebugAssert = x3::rule<class kwDebugAssert, std::string>()
= x3::no_case[x3::lit("Debug.Assert")];
auto const kwDebugPrint = x3::rule<class kwDebugPrint, std::string>()
= x3::no_case[x3::lit("Debug.Print")];
} // namespace vb6_grammar