mirror of
https://github.com/fedapo/vb6-parser.git
synced 2025-12-16 16:27:03 +03:00
First commit
This commit is contained in:
116
CMakeLists.txt
Normal file
116
CMakeLists.txt
Normal 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
26
README.md
Normal 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
131
data/long_source.bas
Normal 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
39
data/prova_form.frm
Normal 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
99
data/prova_module.bas
Normal 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
34
data/prova_project.vbp
Normal 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
65
data/test.bas
Normal 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
|
||||
89
docs/ast_vs_grammar_table.txt
Normal file
89
docs/ast_vs_grammar_table.txt
Normal 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
4
docs/notes_Spirit.md
Normal 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
115
docs/notes_vb6.md
Normal 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
114
docs/vb6_attributes.md
Normal 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
57
msvc/vb6_parser.sln
Normal 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
154
msvc/vb6_parser.vcxproj
Normal 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>
|
||||
42
msvc/vb6_parser.vcxproj.filters
Normal file
42
msvc/vb6_parser.vcxproj.filters
Normal 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
180
msvc/vb6_parser_lib.vcxproj
Normal 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>
|
||||
93
msvc/vb6_parser_lib.vcxproj.filters
Normal file
93
msvc/vb6_parser_lib.vcxproj.filters
Normal 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>
|
||||
168
msvc/vb6_parser_test.vcxproj
Normal file
168
msvc/vb6_parser_test.vcxproj
Normal 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>
|
||||
42
msvc/vb6_parser_test.vcxproj.filters
Normal file
42
msvc/vb6_parser_test.vcxproj.filters
Normal 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
91
src/color_console.cpp
Normal 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
25
src/color_console.hpp
Normal 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
754
src/cpp_ast_printer.cpp
Normal 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
82
src/cpp_ast_printer.hpp
Normal 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
983
src/raw_ast_printer.cpp
Normal 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
81
src/raw_ast_printer.hpp
Normal 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
50
src/test_gosub.cpp
Normal 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
116
src/test_grammar_helper.hpp
Normal 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
716
src/vb6_ast.hpp
Normal 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
285
src/vb6_ast_adapt.hpp
Normal 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
892
src/vb6_ast_printer.cpp
Normal 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
81
src/vb6_ast_printer.hpp
Normal 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
35
src/vb6_config.hpp
Normal 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
42
src/vb6_error_handler.hpp
Normal 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
38
src/vb6_parser.cpp
Normal 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
379
src/vb6_parser.hpp
Normal 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
475
src/vb6_parser_def.hpp
Normal 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
|
||||
}
|
||||
35
src/vb6_parser_functions.cpp
Normal file
35
src/vb6_parser_functions.cpp
Normal 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
23
src/vb6_parser_helper.cpp
Normal 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
153
src/vb6_parser_keywords.hpp
Normal 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
69
src/vb6_parser_main.cpp
Normal 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);
|
||||
}
|
||||
75
src/vb6_parser_operators.hpp
Normal file
75
src/vb6_parser_operators.hpp
Normal 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
|
||||
*/
|
||||
}
|
||||
50
src/vb6_parser_statements.cpp
Normal file
50
src/vb6_parser_statements.cpp
Normal 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)
|
||||
|
||||
}
|
||||
337
src/vb6_parser_statements_def.hpp
Normal file
337
src/vb6_parser_statements_def.hpp
Normal 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
|
||||
)
|
||||
}
|
||||
508
src/vb6_parser_statements_test.cpp
Normal file
508
src/vb6_parser_statements_test.cpp
Normal 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
813
src/vb6_parser_test.cpp
Normal 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");
|
||||
}
|
||||
}
|
||||
29
src/vb6_parser_test_main.cpp
Normal file
29
src/vb6_parser_test_main.cpp
Normal 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
200
src/vb6_test1.cpp
Normal 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
259
src/vb6_test2.cpp
Normal 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
364
src/visual_basic_x3.hpp
Normal 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
|
||||
Reference in New Issue
Block a user