-
Notifications
You must be signed in to change notification settings - Fork 33
/
modQuickConvert.bas
1465 lines (1305 loc) · 57.4 KB
/
modQuickConvert.bas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
Attribute VB_Name = "modQuickConvert"
Option Explicit
Private Const Idnt As Long = 2
Private Const Attr As String = "Attribute"
Private Const Q As String = """"
Private Const A As String = "'"
Private Const S As String = " "
Private Const STRING_TOKEN_PREFIX As String = "__S"
Private Const EXPRESSION_TOKEN_PREFIX As String = "__E_"
Private LineStrings() As String, LineStringsCount As Long
Private LineComment As String
Private LineLineNo As String
Private InProperty As Boolean
Private CurrentTypeName As String
Private CurrentEnumName As String
Private CurrentFunctionName As String
Private CurrentFunctionReturnValue As String
Private CurrentFunctionArgs As String
Private CurrentFunctionArrays As String
Private ModuleName As String
Private ModuleFunctions As String
Private ModuleArrays As String
Private ModuleProperties As String
Private WithVars As String
Public Enum DeclarationType
DECL_GLOBAL = 99
DECL_SIGNATURE = 98
DECL_LOCAL = 1
DECL_TYPE
DECL_ENUM
DECL_EXTERN = 101
End Enum
Public Enum CodeType
CODE_MODULE
CODE_CLASS
CODE_FORM
CODE_CONTROL
End Enum
Public Type RandomType
J As Long
W As String
X As String * 5
End Type
Private Function ResolveSources(ByVal FileName As String) As String
If FileName = "" Then FileName = "prj.vbp"
If FileName = "forms" Then
ResolveSources = VBPForms(True)
ElseIf FileName = "modules" Then
ResolveSources = VBPModules
ElseIf FileName = "classes" Then
ResolveSources = VBPClasses
ElseIf FileName = "usercontrols" Then
ResolveSources = VBPUserControls
Else
If InStr(FileName, "\") = 0 Then FileName = App.Path & "\" & FileName
ResolveSources = IIf(Right(FileName, 4) = ".vbp", VBPCode(FileName), FileName)
End If
End Function
Public Function Convert(Optional ByVal FileName As String = "") As String
Dim FileList As String
FileList = ResolveSources(FileName)
Convert = QuickConvertFiles(FileList)
End Function
Public Function QuickConvertFiles(ByVal List As String) As String
Const lintDotsPerRow As Long = 50
Dim L As Variant
Dim X As Long
Dim StartTime As Date
StartTime = Now
For Each L In Split(List, vbCrLf)
Dim Result As String
Result = QuickConvertFile(L)
If Result <> "" Then
Dim S As String
Debug.Print vbCrLf & "Done (" & DateDiff("s", StartTime, Now) & "s). To re-run for failing file, hit enter on the line below:"
S = "LINT FAILED: " & L & vbCrLf & Result & vbCrLf & "?Lint(""" & L & """)"
QuickConvertFiles = S
Exit Function
Else
Debug.Print Switch(Right(L, 3) = "frm", "o", Right(L, 3) = "cls", "x", Right(L, 3) = "ctl", "+", True, ".");
End If
X = X + 1
If X >= lintDotsPerRow Then X = 0: Debug.Print
DoEvents
Next
Debug.Print vbCrLf & "Done (" & DateDiff("s", StartTime, Now) & "s)."
QuickConvertFiles = ""
End Function
Public Function CodeFileType(ByVal File As String) As CodeType
Select Case Right(LCase(File), 4)
Case ".bas": CodeFileType = CODE_MODULE
Case ".frm": CodeFileType = CODE_FORM
Case ".cls": CodeFileType = CODE_CLASS
Case ".ctl": CodeFileType = CODE_CONTROL
Case Else: CodeFileType = CODE_MODULE
End Select
End Function
Public Function QuickConvertFile(ByVal File As String) As String
ModuleArrays = ""
If InStr(File, "\") = 0 Then File = App.Path & "\" & File
Dim fName As String, Contents As String, GivenName As String, CheckName As String
fName = Mid(File, InStrRev(File, "\") + 1)
CheckName = Replace(Replace(Replace(fName, ".bas", ""), ".cls", ""), ".frm", "")
ErrorPrefix = Right(Space(18) & fName, 18) & " "
Contents = ReadEntireFile(File)
GivenName = GetModuleName(Contents)
If LCase(CheckName) <> LCase(GivenName) Then
QuickConvertFile = "QuickConvertFile: Module name [" & GivenName & "] must match file name [" & RemoveUntil(fName, ".") & "]. Rename module or class to match the other"
Exit Function
End If
QuickConvertFile = ConvertContents(Contents, CodeFileType(File))
End Function
Public Function GetModuleName(ByVal Contents As String) As String
GetModuleName = RegExNMatch(Contents, "Attribute VB_Name = ""([^""]+)""", 0)
GetModuleName = Replace(Replace(GetModuleName, "Attribute VB_Name = ", ""), """", "")
End Function
Public Function I(ByVal N As Long) As String
If N <= 0 Then I = "" Else I = Space(N)
End Function
Public Function ConvertContents(ByVal Contents As String, ByVal vCodeType As CodeType, Optional ByVal SubSegment As Boolean = False) As String
Dim Lines() As String, ActualLine As Variant, LL As String, L As String
'On Error GoTo LintError
If Not SubSegment Then
ModuleName = GetModuleName(Contents)
ModuleFunctions = GetModuleFunctions(Contents)
End If
Lines = Split(Replace(Contents, vbCr, ""), vbLf)
Dim InAttributes As Boolean, InBody As Boolean
InBody = SubSegment
Dim MultiLineOrig As String, MultiLine As String, IsMultiLine As Boolean
Dim LineN As Long, Indent As Long
Dim NewContents As String
Dim SelectHasCase As Boolean
Indent = 0
NewContents = ""
' NewContents = UsingEverything & vbCrLf2
' NewContents = NewContents & "static class " & ModuleName & " {" & vbCrLf
For Each ActualLine In Lines
LL = ActualLine
' If MaxErrors > 0 And ErrorCount >= MaxErrors Then Exit For
IsMultiLine = False
If Right(LL, 2) = " _" Then
Dim Portion As String
Portion = Left(LL, Len(LL) - 2)
MultiLineOrig = MultiLineOrig & LL & vbCrLf
If MultiLine <> "" Then Portion = " " & Trim(Portion)
MultiLine = MultiLine + Portion
LineN = LineN + 1
GoTo NextLineWithoutRecord
ElseIf MultiLine <> "" Then
MultiLineOrig = MultiLineOrig & LL
LL = MultiLine & " " & Trim(LL)
MultiLine = ""
IsMultiLine = True
Else
MultiLineOrig = ""
End If
L = CleanLine(LL)
If Not InBody Then
Dim IsAttribute As Boolean
IsAttribute = StartsWith(LTrim(L), "Attribute ")
If Not InAttributes And IsAttribute Then
InAttributes = True
GoTo NextLineWithoutRecord
ElseIf InAttributes And Not IsAttribute Then
InAttributes = False
InBody = True
LineN = 0
Else
GoTo NextLineWithoutRecord
End If
End If
LineN = LineN + 1
' If LineN >= 357 Then Stop
Dim UnindentedAlready As Boolean
If RegExTest(L, "^[ ]*(Else|ElseIf .* Then)$") Then
Indent = Indent - Idnt
UnindentedAlready = True
ElseIf RegExTest(L, "^[ ]*End Select$") Then
Indent = Indent - Idnt - Idnt
ElseIf RegExTest(L, "^[ ]*(End (If|Function|Sub|Property|Enum|Type|With)|Next( .*)?|Wend|Loop|Loop (While .*|Until .*)|ElseIf .*)$") Then
Indent = Indent - Idnt
UnindentedAlready = True
CurrentEnumName = ""
CurrentTypeName = ""
If RegExTest(L, "^[ ]*End With") Then Stack WithVars
Else
UnindentedAlready = False
End If
Dim NewLine As String
NewLine = ""
If InProperty Then ' we process properties out of band to keep getters and setters together
If InStr(L, "End Property") > 0 Then InProperty = False
GoTo NextLineWithoutRecord
End If
If CurrentTypeName <> "" Then ' if we are in a type or an enum, the entire line is parsed as such
NewLine = NewLine & ConvertTypeLine(L, vCodeType)
ElseIf CurrentEnumName <> "" Then
NewLine = NewLine & ConvertEnumLine(L)
ElseIf RegExTest(L, "^[ ]*If ") Then ' The "If" control structure, when single-line, lacks the "End If" to signal a close.
NewLine = NewLine & ConvertIf(L)
If InStr(L, " Then ") = 0 Then Indent = Indent + Idnt
ElseIf RegExTest(L, "^[ ]*ElseIf .*$") Then
NewLine = NewLine & ConvertIf(L)
If InStr(L, " Then ") = 0 Then Indent = Indent + Idnt
Else
Dim Statements() As String, SSI As Long, St As String
Statements = Split(Trim(L), ": ")
For SSI = LBound(Statements) To UBound(Statements)
St = Statements(SSI)
If RegExTest(St, "^[ ]*ElseIf .*$") Then
NewLine = NewLine & ConvertIf(St)
Indent = Indent + Idnt
ElseIf RegExTest(St, "^[ ]*Else$") Then
NewLine = NewLine & "} else {"
Indent = Indent + Idnt
ElseIf RegExTest(St, "^[ ]*End Function") Then
NewLine = NewLine & "return " & CurrentFunctionReturnValue & ";" & vbCrLf & "}"
CurrentFunctionName = ""
CurrentFunctionReturnValue = ""
CurrentFunctionArrays = ""
If Not UnindentedAlready Then Indent = Indent - Idnt
ElseIf RegExTest(St, "^[ ]*End Select$") Then
NewLine = NewLine & "break;" & vbCrLf
NewLine = NewLine & "}"
If Not UnindentedAlready Then Indent = Indent - Idnt
ElseIf RegExTest(St, "^[ ]*End (If|Sub|Enum|Type)$") Then
CurrentTypeName = ""
CurrentEnumName = ""
NewLine = NewLine & "}"
If Not UnindentedAlready Then Indent = Indent - Idnt
ElseIf RegExTest(St, "^[ ]*For Each") Then
Indent = Indent + Idnt
NewLine = ConvertForEach(St)
ElseIf RegExTest(St, "^[ ]*For ") Then
Indent = Indent + Idnt
NewLine = ConvertFor(St)
ElseIf RegExTest(St, "^[ ]*Next\b") Then
NewLine = NewLine & "}"
If Not UnindentedAlready Then Indent = Indent - Idnt
ElseIf RegExTest(St, "^[ ]*While ") Then
NewLine = NewLine & ConvertWhile(St)
Indent = Indent + Idnt
ElseIf RegExTest(St, "^[ ]*Wend") Then
NewLine = NewLine & "}"
If Not UnindentedAlready Then Indent = Indent - Idnt
ElseIf RegExTest(St, "^[ ]*Do (While|Until)") Then
NewLine = NewLine & ConvertWhile(St)
Indent = Indent + Idnt
ElseIf RegExTest(St, "^[ ]*Loop$") Then
NewLine = NewLine & "}"
ElseIf RegExTest(St, "^[ ]*Do$") Then
NewLine = NewLine & "do {"
Indent = Indent + Idnt
ElseIf RegExTest(St, "^[ ]*(Loop While |Loop Until )") Then
NewLine = NewLine & ConvertWhile(St)
ElseIf RegExTest(St, "^[ ]*With ") Then
NewLine = NewLine & ConvertWith(St)
Indent = Indent + Idnt
ElseIf RegExTest(St, "^[ ]*Select Case ") Then
NewLine = NewLine & ConvertSwitch(St)
Indent = Indent + Idnt + Idnt
SelectHasCase = False
ElseIf RegExTest(St, "^[ ]*Case ") Then
NewLine = NewLine & ConvertSwitchCase(St, SelectHasCase)
SelectHasCase = True
ElseIf RegExTest(St, "^[ ]*(Private |Public )?Declare (Function |Sub )") Then
NewLine = NewLine & ConvertDeclare(St) ' External Api
ElseIf RegExTest(St, "^((Private|Public|Friend) )?Function ") Then
CurrentFunctionArgs = ""
Indent = Indent + Idnt
NewLine = NewLine & ConvertSignature(St, vCodeType)
ElseIf RegExTest(St, "^((Private|Public|Friend) )?Sub ") Then
CurrentFunctionArgs = ""
Indent = Indent + Idnt
NewLine = NewLine & ConvertSignature(St, vCodeType)
ElseIf RegExTest(St, "^((Private|Public|Friend) )?Property (Get|Let|Set) ") Then
CurrentFunctionArgs = ""
NewLine = NewLine & ConvertProperty(St, Contents, vCodeType)
InProperty = Not EndsWith(L, "End Property")
If InProperty Then
Indent = Indent + Idnt
Else
GoTo NextLine
End If
ElseIf RegExTest(St, "^[ ]*(Public |Private )?Enum ") Then
NewLine = NewLine & ConvertEnum(St)
Indent = Indent + Idnt
ElseIf RegExTest(St, "^[ ]*(Public |Private )?Type ") Then
NewLine = NewLine & ConvertType(St)
Indent = Indent + Idnt
ElseIf RegExTest(St, "^[ ]*(Dim|Private|Public|Const|Global|Static) ") Then
NewLine = NewLine & ConvertDeclaration(St, IIf(CurrentFunctionName = "", DECL_GLOBAL, DECL_LOCAL), vCodeType)
Else
NewLine = NewLine & ConvertStatement(St)
End If
NextStatement:
Next
End If
NextLine:
' If IsMultiLine Then Stop
' If InStr(LL, "Function") > 0 Then Stop
' If InStr(LL, "Private Function") > 0 Then Stop
' If Indent < 0 Then Stop
NewLine = Decorate(NewLine)
If Trim(NewLine) <> "" Then
NewContents = NewContents & I(Indent) & NewLine & vbCrLf
End If
NextLineWithoutRecord:
Next
' If AutoFix <> "" Then WriteFile AutoFix, Left(NewContents, Len(NewContents) - 2), True
' NewContents = NewContents & "}" & vbCrLf
ConvertContents = NewContents
Exit Function
LintError:
Debug.Print "Error in quick convert [" & Err.Number & "]: " & Err.Description
ConvertContents = "Error in quick convert [" & Err.Number & "]: " & Err.Description
End Function
Private Function ReadEntireFile(ByVal tFileName As String) As String
On Error Resume Next
Dim mFSO As Object
Set mFSO = CreateObject("Scripting.FileSystemObject")
ReadEntireFile = mFSO.OpenTextFile(tFileName, 1).ReadAll
If FileLen(tFileName) / 10 <> Len(ReadEntireFile) / 10 Then
MsgBox "ReadEntireFile was short: " & FileLen(tFileName) & " vs " & Len(ReadEntireFile)
End If
End Function
' de string and decomment a given line (before conversion)
Public Function CleanLine(ByVal Line As String) As String
Dim X As Long, Y As Long, Token As String, Value As String
Erase LineStrings
LineStringsCount = 0
LineComment = ""
LineLineNo = ""
If RegExTest(Line, "^[0-9]+\b") Then
LineLineNo = RegExNMatch(Line, "^([0-9]+)\b")
Line = Trim(Mid(Line, Len(LineLineNo) + 1))
End If
Do While True
X = InStr(Line, Q)
If X = 0 Then Exit Do
Y = InStr(X + 1, Line, Q)
Do While Mid(Line, Y + 1, 1) = Q
Y = InStr(Y + 2, Line, Q)
Loop
If Y = 0 Then Exit Do
LineStringsCount = LineStringsCount + 1
ReDim Preserve LineStrings(1 To LineStringsCount)
Value = ConvertStringLiteral(Mid(Line, X, Y - X + 1))
LineStrings(LineStringsCount) = Value
Token = STRING_TOKEN_PREFIX & LineStringsCount
Line = Left(Line, X - 1) & Token & Mid(Line, Y + 1)
Loop
If LCase(Left(LTrim(Line), 4)) = "rem " Then
LineComment = Line
Line = ""
Else
X = InStr(Line, A)
If X > 0 Then
LineComment = Trim(Mid(Line, X + 1))
Line = RTrim(Left(Line, X - 1))
End If
End If
CleanLine = Line
End Function
' re-string and re-comment a given line (after conversion)
Public Function Decorate(ByVal Line As String) As String
Dim I As Long
For I = LineStringsCount To 1 Step -1
Line = Replace(Line, "__S" & I, LineStrings(I))
Next
If LineComment <> "" Then Line = Line & " // " & LineComment
Decorate = Line
End Function
Public Function ConvertStringLiteral(ByVal L As String) As String
L = Replace(L, "\", "\\")
L = """" & Replace(Mid(L, 2, Len(L) - 2), """""", "\""") & """"
ConvertStringLiteral = L
End Function
Public Function StartsWith(ByVal L As String, ByVal Find As String) As Boolean
StartsWith = Left(L, Len(Find)) = Find
End Function
Public Function EndsWith(ByVal L As String, ByVal Find As String) As Boolean
EndsWith = Right(L, Len(Find)) = Find
End Function
Public Function StripLeft(ByVal L As String, ByVal Find As String) As String
If StartsWith(L, Find) Then StripLeft = Mid(L, Len(Find) + 1) Else StripLeft = L
End Function
Public Function RecordLeft(ByRef L As String, ByVal Find As String) As Boolean
RecordLeft = StartsWith(L, Find)
If RecordLeft Then L = Mid(L, Len(Find) + 1)
End Function
Public Function RemoveUntil(ByRef L As String, ByVal Find As String, Optional ByVal RemoveFind As Boolean = False) As String
Dim IX As Long
IX = InStr(L, Find)
If IX <= 0 Then Exit Function
RemoveUntil = Left(L, IX - 1)
L = Mid(L, IIf(RemoveFind, IX + Len(Find), IX))
End Function
Private Function GetModuleFunctions(ByVal Contents As String) As String
Const Pattern As String = "(Private (Function|Sub) [^(]+\()"
Dim N As Long, I As Long
Dim S As String
N = RegExCount(Contents, Pattern)
GetModuleFunctions = ""
For I = 0 To N - 1
S = RegExNMatch(Contents, Pattern, I)
S = Replace(S, "Private ", "")
S = Replace(S, "Sub ", "")
S = Replace(S, "Function ", "")
S = Replace(S, "(", "")
GetModuleFunctions = GetModuleFunctions & "[" & S & "]"
Next
End Function
Private Function IsLocalFuncRef(ByVal F As String) As Boolean
IsLocalFuncRef = InStr(ModuleFunctions, "[" & Trim(F) & "]") <> 0
End Function
Private Function SearchLeft(ByVal Start As Long, ByVal Src As String, ByVal Find As String, Optional ByVal NotIn As Boolean = False, Optional ByVal Reverse As Boolean = False) As Long
Dim Bg As Long, Ed As Long, St As Long
Dim I As Long, C As String, Found As Boolean
If Not Reverse Then
Bg = IIf(Start = 0, 1, Start)
Ed = Len(Src)
St = 1
Else
Bg = IIf(Start = 0, Len(Src), Start)
Ed = 1
St = -1
End If
For I = Bg To Ed Step St
C = Mid(Src, I, 1)
Found = InStr(Find, C) > 0
If Not NotIn And Found Or NotIn And Not Found Then
SearchLeft = I
Exit Function
End If
Next
SearchLeft = 0
End Function
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Function ConvertIf(ByVal L As String) As String
Dim ixThen As Long, Expression As String
Dim WithThen As Boolean, WithElse As Boolean
Dim MultiStatement As Boolean
L = Trim(L)
ixThen = InStr(L, " Then")
WithThen = InStr(L, " Then ") > 0
WithElse = InStr(L, " Else ") > 0
Expression = Trim(Left(L, ixThen - 1))
Expression = StripLeft(Expression, "If ")
Expression = StripLeft(Expression, "ElseIf ")
ConvertIf = IIf(Not IsInStr(L, "ElseIf"), "if", "} else if")
ConvertIf = ConvertIf & "(" & ConvertExpression(Expression) & ")"
If Not WithThen Then
ConvertIf = ConvertIf & " {"
Else
Dim cThen As String, cElse As String
cThen = Trim(Mid(L, ixThen + 5))
Dim ixElse As Long
ixElse = InStr(cThen, " Else ")
If ixElse > 0 Then
cElse = Mid(cThen, ixElse + 6)
cThen = Left(cThen, ixElse - 1)
Else
cElse = ""
End If
' Inline Then
Dim St As Variant
MultiStatement = InStr(cThen, ": ") > 0
If MultiStatement Then
ConvertIf = ConvertIf & " { "
For Each St In Split(cThen, ": ")
ConvertIf = ConvertIf & ConvertStatement(St) & " "
Next
ConvertIf = ConvertIf & "}"
Else
ConvertIf = ConvertIf & ConvertStatement(cThen)
End If
' Inline Then ... Else
If ixElse > 0 Then
MultiStatement = InStr(cElse, ":") > 0
If MultiStatement Then
ConvertIf = ConvertIf & " else { "
For Each St In Split(cElse, ":")
ConvertIf = ConvertIf & ConvertStatement(Trim(St))
Next
ConvertIf = ConvertIf & " }"
Else
ConvertIf = ConvertIf & " else " & ConvertStatement(cElse)
End If
End If
End If
End Function
Public Function ConvertWith(ByVal L As String) As String
Dim Value As String
Value = Trim(L)
Value = StripLeft(Value, "With ")
If ValueIsSimple(Value) Then
WithVars = Stack(WithVars, ConvertExpression(Value))
ConvertWith = "// Converted WITH statement: " & L
Else
Dim WithVar As String
WithVar = "__withVar" & Random(1000)
ConvertWith = ""
ConvertWith = ConvertWith & "// " & L & " // TODO (not supported): Expression used in WITH. Verify result: " + Value
ConvertWith = ConvertWith & vbCrLf & "dynamic " & WithVar & " = " & ConvertStatement(Value) & ";"
WithVars = Stack(WithVars, WithVar)
End If
End Function
Public Function WithVar() As String
WithVar = Stack(WithVars, , True)
End Function
Public Function ConvertSwitch(ByVal L As String) As String
ConvertSwitch = "switch(" & ConvertExpression(Trim(Replace(L, "Select Case ", ""))) & ") {"
End Function
Public Function ConvertSwitchCase(ByVal L As String, ByVal SelectHasCase As Boolean) As String
Dim V As Variant
ConvertSwitchCase = ""
If SelectHasCase Then ConvertSwitchCase = ConvertSwitchCase & "break;" & vbCrLf
If Trim(L) = "Case Else" Then
ConvertSwitchCase = ConvertSwitchCase & "default: "
Else
RecordLeft L, "Case "
If Right(L, 1) = ":" Then L = Left(L, Len(L) - 1)
For Each V In Split(L, ", ")
V = Trim(V)
If InStr(V, " To ") > 0 Then
ConvertSwitchCase = ConvertSwitchCase & "default: /* TODO: Cannot Convert Ranged Case: " & L & " */"
ElseIf StartsWith(V, "Is ") Then
ConvertSwitchCase = ConvertSwitchCase & "default: /* TODO: Cannot Convert Expression Case: " & L & " */"
Else
ConvertSwitchCase = ConvertSwitchCase & "case " & ConvertExpression(V) & ": "
End If
Next
End If
End Function
Public Function ConvertWhile(ByVal L As String) As String
Dim Exp As String, Closing As Boolean, Invert As Boolean
L = LTrim(L)
If RecordLeft(L, "Do While ") Then
Exp = L
ElseIf RecordLeft(L, "Do Until ") Then
Exp = L
Invert = True
ElseIf RecordLeft(L, "While ") Then
Exp = L
ElseIf RecordLeft(L, "Loop While ") Then
Exp = L
Closing = True
ElseIf RecordLeft(L, "Loop Until ") Then
Exp = L
Closing = True
Invert = True
End If
ConvertWhile = ""
If Closing Then ConvertWhile = ConvertWhile & "} "
ConvertWhile = ConvertWhile & "while("
If Invert Then ConvertWhile = ConvertWhile & "!("
ConvertWhile = ConvertWhile & ConvertExpression(Exp)
If Invert Then ConvertWhile = ConvertWhile & ")"
ConvertWhile = ConvertWhile & ")"
If Not Closing Then ConvertWhile = ConvertWhile & " {" Else ConvertWhile = ConvertWhile & ";"
End Function
Public Function ConvertFor(ByVal L As String) As String
Dim Var As String, ForFrom As String, ForTo As String, ForStep As String
Dim ForReverse As Boolean, ForCheck As String
L = Trim(L)
RecordLeft L, "For "
Var = RemoveUntil(L, " = ", True)
ForFrom = RemoveUntil(L, " To ", True)
ForTo = L
ForStep = RemoveUntil(ForTo, " Step ", True)
If ForStep = "" Then ForStep = "1"
ForStep = ConvertExpression(ForStep)
ForReverse = InStr(ForStep, "-") > 0
If ForReverse Then ForCheck = " >= " Else ForCheck = " <= "
ConvertFor = ""
ConvertFor = ConvertFor & "for ("
ConvertFor = ConvertFor & ExpandToken(Var) & " = " & ConvertExpression(ForFrom) & "; "
ConvertFor = ConvertFor & ExpandToken(Var) & ForCheck & ConvertExpression(ForTo) & "; "
ConvertFor = ConvertFor & ExpandToken(Var) & " += " & ForStep
ConvertFor = ConvertFor & ") {"
End Function
Public Function ConvertForEach(ByVal L As String) As String
Dim Var As String, ForSource As String
L = Trim(L)
RecordLeft L, "For "
RecordLeft L, "Each "
Var = RemoveUntil(L, " In ", True)
ForSource = L
ConvertForEach = ConvertForEach & "foreach (var iter" & Var & " in " & ConvertExpression(ForSource) & ") {" & vbCrLf & Var & " = iter" & Var & ";"
End Function
Public Function ConvertType(ByVal L As String) As String
Dim isPrivate As Boolean, isPublic As Boolean
isPublic = RecordLeft(L, "Public ")
isPrivate = RecordLeft(L, "Private ")
RecordLeft L, "Type "
CurrentTypeName = L
ConvertType = ""
If Not isPrivate Then ConvertType = ConvertType & "public "
ConvertType = ConvertType & "class " ' `struct ` is available, but leads to non-conforming behavior when indexing in lists...
ConvertType = ConvertType & L
ConvertType = ConvertType & "{ "
End Function
Public Function ConvertTypeLine(ByVal L As String, ByVal vCodeType As CodeType) As String
ConvertTypeLine = ConvertDeclaration(L, DECL_TYPE, vCodeType)
End Function
Public Function ConvertEnum(ByVal L As String) As String
Dim isPrivate As Boolean, isPublic As Boolean
isPublic = RecordLeft(L, "Public ")
isPrivate = RecordLeft(L, "Private ")
RecordLeft L, "Enum "
CurrentEnumName = L
ConvertEnum = ""
If Not isPrivate Then ConvertEnum = ConvertEnum & "public "
ConvertEnum = ConvertEnum & "enum "
ConvertEnum = ConvertEnum & L
ConvertEnum = ConvertEnum & "{ "
End Function
Public Function ConvertEnumLine(ByVal L As String) As String
Dim Name As String, Value As String
Dim Parts() As String
If Trim(L) = "" Then Exit Function
Parts = Split(L, " = ")
Name = Trim(Parts(0))
If UBound(Parts) >= 1 Then Value = Trim(Parts(1)) Else Value = ""
ConvertEnumLine = ""
If Right(CurrentEnumName, 1) = "+" Then ConvertEnumLine = ConvertEnumLine & ", "
ConvertEnumLine = ConvertEnumLine & Name
If Value <> "" Then ConvertEnumLine = ConvertEnumLine & " = " & ConvertExpression(Value)
CurrentEnumName = CurrentEnumName & "+" ' convenience
End Function
Public Function ConvertProperty(ByVal L As String, ByVal FullContents As String, ByVal vCodeType As CodeType) As String
Dim Name As String, IX As Long, isPrivate As Boolean, ReturnType As String, Discard As String
Dim PropertyType As String
Dim GetContents As String, SetContents As String
IX = InStr(L, "(")
Name = Left(L, IX - 1)
RecordLeft L, "Public "
isPrivate = RecordLeft(L, "Private ")
RecordLeft L, "Property "
RecordLeft L, "Get "
RecordLeft L, "Let "
RecordLeft L, "Set "
IX = InStr(L, "(")
Name = Left(L, IX - 1)
If InStr(ModuleProperties, Name) > 0 Then Exit Function
CurrentFunctionName = Name
CurrentFunctionReturnValue = "_" & Name
ModuleProperties = ModuleProperties & "[" & Name & "]"
GetContents = FindPropertyBody(FullContents, "Get", Name, ReturnType)
If GetContents <> "" Then GetContents = ConvertContents(GetContents, vCodeType, True)
If ReturnType = "" Then ReturnType = "Variant"
SetContents = FindPropertyBody(FullContents, "Let", Name, Discard)
If SetContents = "" Then SetContents = FindPropertyBody(FullContents, "Set", Name, Discard)
If SetContents <> "" Then SetContents = ConvertContents(SetContents, vCodeType, True)
PropertyType = ConvertArgType(Name, ReturnType)
ConvertProperty = ""
ConvertProperty = ConvertProperty & IIf(isPrivate, "private ", "public ")
ConvertProperty = ConvertProperty & IIf(vCodeType = CODE_MODULE, "static ", "")
ConvertProperty = ConvertProperty & PropertyType & " " & Name & "{ " & vbCrLf
If GetContents <> "" Then
ConvertProperty = ConvertProperty & "get {" & vbCrLf
ConvertProperty = ConvertProperty & PropertyType & " " & CurrentFunctionReturnValue & " = default(" & PropertyType & ");" & vbCrLf
ConvertProperty = ConvertProperty & GetContents
ConvertProperty = ConvertProperty & "return " & CurrentFunctionReturnValue & ";" & vbCrLf
ConvertProperty = ConvertProperty & "}" & vbCrLf
End If
If SetContents <> "" Then
ConvertProperty = ConvertProperty & "set {" & vbCrLf
ConvertProperty = ConvertProperty & SetContents
ConvertProperty = ConvertProperty & "}" & vbCrLf
End If
ConvertProperty = ConvertProperty & "}" & vbCrLf
End Function
Public Function FindPropertyBody(ByVal FullContents As String, ByVal Typ As String, ByVal Name As String, ByRef ReturnType As String) As String
Dim X As Long
X = InStr(FullContents, "Property " & Typ & " " & Name)
If X = 0 Then Exit Function
FindPropertyBody = Mid(FullContents, X)
X = RegExNPos(FindPropertyBody, "\bEnd Property\b", 0)
FindPropertyBody = Trim(Left(FindPropertyBody, X - 1))
RecordLeft FindPropertyBody, "Property " & Typ & " " & Name
RecordLeft FindPropertyBody, "("
X = 1
Do While X > 0
If Left(FindPropertyBody, 1) = "(" Then X = X + 1
If Left(FindPropertyBody, 1) = ")" Then X = X - 1
FindPropertyBody = Mid(FindPropertyBody, 2)
Loop
FindPropertyBody = Trim(FindPropertyBody)
If StartsWith(FindPropertyBody, "As ") Then
FindPropertyBody = Mid(FindPropertyBody, 4)
X = SearchLeft(1, FindPropertyBody, ": " & vbCrLf, False, False)
ReturnType = Left(FindPropertyBody, X - 1)
FindPropertyBody = Mid(FindPropertyBody, X)
End If
Do While StartsWith(FindPropertyBody, vbCrLf): FindPropertyBody = Mid(FindPropertyBody, 3): Loop
Do While Right(FindPropertyBody, 2) = vbCrLf: FindPropertyBody = Left(FindPropertyBody, Len(FindPropertyBody) - 2): Loop
If StartsWith(FindPropertyBody, ":") Then FindPropertyBody = Trim(Mid(FindPropertyBody, 2))
If Right(FindPropertyBody, 1) = ":" Then FindPropertyBody = Trim(Left(FindPropertyBody, Len(FindPropertyBody) - 1))
End Function
Public Function ConvertDeclaration(ByVal L As String, ByVal declType As DeclarationType, ByVal vCodeType As CodeType) As String
Dim IsDim As Boolean, isPrivate As Boolean, isPublic As Boolean, IsConst As Boolean, isGlobal As Boolean, isStatic As Boolean
Dim IsOptional As Boolean, IsByVal As Boolean, IsByRef As Boolean, IsParamArray As Boolean
Dim IsWithEvents As Boolean, IsEvent As Boolean
Dim FixedLength As Long, IsNewable As Boolean
L = Trim(L)
If L = "" Then Exit Function
IsDim = RecordLeft(L, "Dim ")
isPrivate = RecordLeft(L, "Private ")
isPublic = RecordLeft(L, "Public ")
isGlobal = RecordLeft(L, "Global ")
IsConst = RecordLeft(L, "Const ")
isStatic = RecordLeft(L, "Static ")
' If IsInStr(L, "LineStrings") Then Stop
If isStatic And declType = DECL_LOCAL Then LineComment = LineComment & " TODO: (NOT SUPPORTED) C# Does not support static local variables."
Dim Item As Variant, LL As String
For Each Item In Split(L, ", ")
Dim IX As Long, ArgName As String, ArgType As String, ArgDefault As String, IsArray As Boolean, IsReferencableType As Boolean
Dim ArgTargetType As String
Dim StandardEvent As Boolean
If ConvertDeclaration <> "" And declType <> DECL_SIGNATURE And declType <> DECL_EXTERN Then ConvertDeclaration = ConvertDeclaration & vbCrLf
LL = Item
IsEvent = RecordLeft(LL, "Event ")
IsWithEvents = RecordLeft(LL, "WithEvents ")
IsOptional = RecordLeft(LL, "Optional ")
IsByVal = RecordLeft(LL, "ByVal ")
IsByRef = RecordLeft(LL, "ByRef ")
IsParamArray = RecordLeft(LL, "ParamArray ")
IX = InStr(LL, " = ")
If IX > 0 Then
ArgDefault = Trim(Mid(LL, IX + 3))
LL = Left(LL, IX - 1)
Else
ArgDefault = ""
End If
IX = InStr(LL, " As ")
If IX > 0 Then
ArgType = Trim(Mid(LL, IX + 4))
LL = Left(LL, IX - 1)
Else
ArgType = ""
End If
If StartsWith(ArgType, "New ") Then
IsNewable = True
RecordLeft ArgType, "New "
LineComment = LineComment & "TODO: (NOT SUPPORTED) Dimmable 'New' not supported on variable declaration. Instantiated only on declaration. Please ensure usages"
End If
If InStr(ArgType, " * ") > 0 Then
FixedLength = Val(Trim(Mid(ArgType, InStr(ArgType, " * ") + 3)))
ArgType = RemoveUntil(ArgType, " * ")
LineComment = LineComment & "TODO: (NOT SUPPORTED) Fixed Length String not supported: " & ArgName & "(" & FixedLength & ")"
End If
ArgTargetType = ConvertArgType(ArgName, ArgType)
ArgName = LL
If Right(ArgName, 2) = "()" Then
IsArray = True
ArgName = Left(ArgName, Len(ArgName) - 2)
ElseIf RegExTest(ArgName, "^[a-zA-Z_][a-zA-Z_0-9]*\(.* To .*\)$") Then
IsArray = True
LineComment = LineComment & " TODO: (NOT SUPPORTED) Array ranges not supported: " & ArgName
ArgName = RemoveUntil(ArgName, "(")
Else
IsArray = False
End If
IsReferencableType = ArgTargetType = "Recordset" Or ArgTargetType = "Collection"
ArgTargetType = ConvertArgType(ArgName, ArgType)
StandardEvent = IsStandardEvent(ArgName, ArgType)
Select Case (declType)
Case DECL_GLOBAL ' global
If isPublic Or IsDim Then
ConvertDeclaration = ConvertDeclaration & "public "
If vCodeType = CODE_MODULE And Not IsConst Then ConvertDeclaration = ConvertDeclaration & "static "
Else
ConvertDeclaration = ConvertDeclaration & "public " & IIf(Not IsConst, "static ", "")
End If
If IsConst Then ConvertDeclaration = ConvertDeclaration & "const "
ConvertDeclaration = ConvertDeclaration & IIf(IsArray, "List<" & ArgTargetType & ">", ArgTargetType) & " "
ConvertDeclaration = ConvertDeclaration & ArgName
If ArgDefault <> "" Then
ConvertDeclaration = ConvertDeclaration & " = " & ConvertExpression(ArgDefault)
Else
ConvertDeclaration = ConvertDeclaration & " = " & ArgTypeDefault(ArgTargetType, IsArray, IsNewable) ' VB6 always initializes variables on declaration
End If
ConvertDeclaration = ConvertDeclaration & ";"
If IsArray Then ModuleArrays = ModuleArrays & "[" & ArgName & "]"
Case DECL_LOCAL ' function contents
ConvertDeclaration = ConvertDeclaration & IIf(IsArray, "List<" & ArgTargetType & ">", ArgTargetType) & " "
ConvertDeclaration = ConvertDeclaration & ArgName
If ArgDefault <> "" Then
ConvertDeclaration = ConvertDeclaration & " = " & ConvertExpression(ArgDefault)
Else
ConvertDeclaration = ConvertDeclaration & " = " & ArgTypeDefault(ArgTargetType, IsArray, IsNewable) ' VB6 always initializes variables on declaration
End If
ConvertDeclaration = ConvertDeclaration & ";"
If IsArray Or IsReferencableType Then CurrentFunctionArrays = CurrentFunctionArrays & "[" & ArgName & "]"
CurrentFunctionArgs = CurrentFunctionArgs & "[" & ArgName & "]"
Case DECL_SIGNATURE ' sig def
If ConvertDeclaration <> "" Then ConvertDeclaration = ConvertDeclaration & ", "
If IsByRef Or Not IsByVal Then ConvertDeclaration = ConvertDeclaration & "ref "
ConvertDeclaration = ConvertDeclaration & IIf(IsArray, "List<" & ArgTargetType & ">", ArgTargetType) & " "
ConvertDeclaration = ConvertDeclaration & ArgName
If ArgDefault <> "" Then ConvertDeclaration = ConvertDeclaration & " = " & ConvertExpression(ArgDefault) ' default on method sig means optional param
If IsArray Or IsReferencableType Then CurrentFunctionArrays = CurrentFunctionArrays & "[" & ArgName & "]"
CurrentFunctionArgs = CurrentFunctionArgs & "[" & ArgName & "]"
Case DECL_TYPE
ConvertDeclaration = ConvertDeclaration & "public " & ArgTargetType & " " & ArgName & ";"
Case DECL_ENUM
Case DECL_EXTERN
If ConvertDeclaration <> "" Then ConvertDeclaration = ConvertDeclaration & ", "
If IsByRef Or Not IsByVal Then ConvertDeclaration = ConvertDeclaration & "ref "
ConvertDeclaration = ConvertDeclaration & IIf(IsArray, "List<" & ArgTargetType & ">", ArgTargetType) & " "
ConvertDeclaration = ConvertDeclaration & ArgName
End Select
' If IsParamArray Then Stop
If ArgType = "" And Not IsEvent And Not StandardEvent Then
End If
If declType = DECL_SIGNATURE Then
If IsParamArray Then
Else
If Not IsByVal And Not IsByRef And Not StandardEvent Then
End If
End If
If IsOptional And IsByRef Then
End If
If IsOptional And ArgDefault = "" Then
End If
End If
Next
End Function
'Function IsStandardEvent(ByVal ArgName As String, ByVal ArgType As String) As Boolean
' If ArgName = "Cancel" Then IsStandardEvent = True: Exit Function
' If ArgName = "LastRow" Then IsStandardEvent = True: Exit Function
' If ArgName = "LastCol" Then IsStandardEvent = True: Exit Function
' If ArgName = "newCol" Then IsStandardEvent = True: Exit Function
' If ArgName = "newCol" Then IsStandardEvent = True: Exit Function
' If ArgName = "newRow" Then IsStandardEvent = True: Exit Function
' If ArgName = "OldValue" Then IsStandardEvent = True: Exit Function
' If ArgName = "Index" And ArgType = "Integer" Then IsStandardEvent = True: Exit Function
' If ArgName = "Offset" And ArgType = "Integer" Then IsStandardEvent = True: Exit Function
' If ArgName = "UnloadMode" And ArgType = "Integer" Then IsStandardEvent = True: Exit Function
' If ArgName = "KeyCode" And ArgType = "Integer" Then IsStandardEvent = True: Exit Function
' If ArgName = "KeyAscii" And ArgType = "Integer" Then IsStandardEvent = True: Exit Function
' If ArgName = "Button" And ArgType = "Integer" Then IsStandardEvent = True: Exit Function
' If ArgName = "Shift" And ArgType = "Integer" Then IsStandardEvent = True: Exit Function
' If ArgName = "X" And ArgType = "Single" Then IsStandardEvent = True: Exit Function
' If ArgName = "Y" And ArgType = "Single" Then IsStandardEvent = True: Exit Function
' If ArgName = "Source" And ArgType = "Control" Then IsStandardEvent = True: Exit Function
' If ArgName = "Item" And ArgType = "Integer" Then IsStandardEvent = True: Exit Function
' IsStandardEvent = False
'End Function
'
Public Function ConvertArgType(ByVal Name As String, ByVal Typ As String) As String
Select Case Typ
Case "Long", "Integer", "Int32", "Short":
ConvertArgType = "int"
Case "Currency"
ConvertArgType = "decimal"
Case "Date"
ConvertArgType = "DateTime"
Case "Double", "Float", "Single"
ConvertArgType = "decimal"
Case "String":
ConvertArgType = "string"
Case "Boolean"
ConvertArgType = "bool"
Case "Variant", "Object"
ConvertArgType = "dynamic"
Case Else
ConvertArgType = Typ
End Select
End Function
Public Function ArgTypeDefault(ByVal ArgType As String, Optional ByVal asArray As Boolean = False, Optional ByVal IsNewable As Boolean = False) As String
If Not asArray Then
Select Case LCase(ArgType)
Case "string"
ArgTypeDefault = """"""
Case "long", "int", "integer", "short", "byte", "decimal", "float", "double", "currency"
ArgTypeDefault = "0"
Case "boolean", "bool"
ArgTypeDefault = "false"
Case "vbtristate"
ArgTypeDefault = "vbUseDefault"
Case "datetime", "date"
ArgTypeDefault = "DateTime.MinValue"