-
1 Attachment(s)
VB6 Automated Source Code Processing Helper
WORK IN PROGRESS
This is a work in progress - it does a pretty good job of doing what it is trying to do, but there are definitely holes in the implementation. If you can provide any source code that fails, I will be happy to fix my code to accommodate it!
WHAT IS THIS?
This project parses VBG & VBP files, building a list of all source code files (with limitations, see the KNOWN/EXPECTED PROBLEMS section below). You can then loop through each source file, and each line of code therein to perform just about any line-level custom processing you can imagine. The project includes fast PCRE2 regex support for finding matching lines of code (in forward and reverse directions).
When you find a line you are looking for, you can optionally replace the text with something else, and when you are done you can save the file back to disk.
A VERY VERY VERY IMPORTANT WARNING
YOU can destroy your source code files using this project as it allows you to modify source code and write it back to disk! Bugs (mine or yours) can be catastrophic, so please make sure you backup your source code before processing it!
NOTE: The included example parser does not modify source files and in my opinion can be considered safe for all uses. That said, I still always recommend having backups made before using this project on your source code.
I TAKE NO RESPONSIBILITY FOR ANY TERRORS THIS PROJECT MAY UNLEASH UPON YOUR COMPUTER/FILES - YOU HAVE BEEN WARNED!
EXAMPLE USES
I use this project for a number of processes on my main project, including:
- Find methods that call "Erl" but contain lines that are not numbered
- Finding missing GUIDs in methods where I reference a method GUID for logging/debugging purposes.
- Finding duplicate GUIDs (copy & paste errors) in lists of field codes, and replacing duplicates with new GUIDs.
- Finding methods that do not have an error handler active.
- Finding methods where I accidentally used the NEW keyword instead of RC5 regfree instantiation (which would cause problems after deployment since the libraries aren't registered on the end-user's computer).
KNOWN/EXPECTED PROBLEMS
I've only tested this project on my own source code, so I expect problems when you try it on your own projects with your own coding styles. I'm happy to fix these issues, especially if you provide code examples.
Handling source code saved in other locales has not been tested at all, and based on the conversation in this thread, I expect problems (possibly serious ones!).
The project currently only supports FRM, BAS, CLS, and CTL files. More exotic things like data reports, web classes, property pages, etc... are not yet supported.
PRE-REQUISITES
IMPORTANT: Ensure that all of the above DLLs are saved in the App\System\ folder.
For Krool's OCXs you will need to register both OCX files.
For Olaf's RC6, you will need to ensure you have registered it properly using the .BAT file included with the binary distribution.
USAGE
There's an included example processor class (CExampleProcessor) included in the project that demonstrates some of the features of the project. The example processor finds methods that do NOT include an "ON ERROR GOTO <label>" line, so it is useful for finding places you forgot to include an error handler.
To try it out, start the project then click Select Files and select any VBP or VBG file.
Once the list of files is parsed and populated, click the Run button and wait while the source files are processed by the CExampleProcessor class.
After processing is complete, click Copy to Clipboard to copy the report/log to the clipboard and paste it into your favourite text editor for review. In particular, look for WARNING lines.
BUILDING YOUR OWN SOURCE CODE PROCESSORS
NOTE: I'll expand on these instructions as the project matures.
To build your own source processor, add a class to the main VB6SourceProcessor project and implement the ISourceProcessor interface in the new class. In the ISourceProcessor_ProcessFile method, perform whatever physical line level pattern matching & processing you desire.
Remember to instantiate your class and call .ProcessFile on it in the frmMain.cmdRun_Click event!
FUTURE ENHANCEMENTS
- I would like to offer the ability to backup original source files before writing modified versions back to disk.
- I would like to add the ability to filter log messages by level (INFO, IMPORTANT, WARNING, ERROR), and maybe by keyword. ADDED IN LATEST VERSION
- I want to add the ability to cancel processing. ADDED IN LATEST VERSION
I imagine more enhancement ideas will come as people start using the project, so please let me know if you have any ideas on how to make the project better.
PROJECT SOURCE CODE
LATEST SOURCE CODE HERE: Attachment 195022
Hope some of you find this project useful!
-
Re: VB6 Automated Source Code Processing Helper
Hi jpbo, nice to see you, I haven't tested your code yet, but it looks great. I'm currently trying to develop WebPage Builder with RC5.Cairo. After a few days, I'll test your code carefully. Thank you, jpbro.
Merry Christmas and Happy New Year.
-
Re: VB6 Automated Source Code Processing Helper
Hi dreammanor - thanks for stopping by :) I haven't been posting too much at the forums lately as I've been busy with work, but I thought I'd take a day to clean up this old project of mine and post here as a small gift to the community. I hope you find it useful!
Just please heed my warning - this project is potentially dangerous as it allows you to mess around with source code outside of the IDE, so please make sure you backup your code before you try anything out!
I'd also love to see some small sample projects with Chinese characters to see how my project handles it - if you can post any small examples, I'd really appreciate it.
Your web page builder project sounds very ambitious, and I wish you all the best.
Merry Christmas and Happy New Year to you too, and best of luck to you and your family this coming year and beyond!
-
Re: VB6 Automated Source Code Processing Helper
Thank you jpbro, and wish you and your family good health and happiness forever.
-
Re: VB6 Automated Source Code Processing Helper
Updated to fix a problem handling Unicode paths.
-
Re: VB6 Automated Source Code Processing Helper
Hi jpbro, I tested your code today. The project can successfully open a VBP file, but when I press the "Run" button, the following error message appears:
Could not initialize PCRE2 library! Last DLL Error: 0
Error code:
Code:
Public Function RegexMatchLine(ByVal p_StartAtOneBasedLineIndex As Long, ByVal p_Regex As String, Optional ByVal p_CaseSensitive As Boolean) As Long
' Return Line # of first found match
Dim ii As Long
If mo_Regex Is Nothing Then
Set mo_Regex = New VBPCRE2.cPcre2
End If
If p_CaseSensitive Then
mo_Regex.IgnoreCase = False
Else
mo_Regex.IgnoreCase = True
End If
For ii = p_StartAtOneBasedLineIndex To Me.PhysicalLineCount
Set mo_LastMatch = mo_Regex.Match(Me.PhysicalLineContent(ii), p_Regex)
If mo_LastMatch.Count Then
RegexMatchLine = ii
Exit Function
End If
Next ii
End Function
After compiling into the executable file, the same error still occurs. (Note: DLL files are already registered correctly)
-
Re: VB6 Automated Source Code Processing Helper
Hi dreammanor, thanks for trying out the project. Do you have the pcre2-16.dll file in the same folder as your registered VbPcre2.dll? pcre2-16.dll is available here: https://github.com/jpbro/VbPcre2/tree/master/bin
-
2 Attachment(s)
Re: VB6 Automated Source Code Processing Helper
I've just update the project in the first post.
It includes a number of improvements:
- You will now see a warning when you click the Run button, just to make sure you understand the risks associated with using this tool:
Attachment 164299
- If you don't want to see that warning, you can disable it by returning FALSE in modOptions.optRequireRunConfirmation()
- After parsing & processing files, a message is shown with a bit of information about the processing/parsing:
Attachment 164301
- There's a new tab based interface that better separates the File & Log grids.
- I've added a bunch of comments to the source (more to come).
- I've made an initial stab at parsing Physical Lines to Logical Lines - this is a work in progress, and there will likely be bugs. If you have any projects that generate ERRORs in the log after clicking the Run button I would love to see them so I can fix the parsing.
Couple of thoughts:
- I don't know if "parsing" is the right word for what I'm doing with the source files when determining the logical lines. I'm open to a correction here, and I will adjust the language in the project accordingly.
- For logical lines, I've come up with the following list. Have I missed anything? Are there better/more correct names to use for any of these?
List of Logical Line "Types"
Code:
logicallinetype_Empty = &H1& ' Blank line (empty or whitespace only)
logicallinetype_Header = &H2& ' Stuff in the header section of an FRM file, or "Attributes"
logicallinetype_Attribute = &H4& ' Hidden VB attribute (e.g. ProcedureID)
logicallinetype_Option = &H8& ' e.g. Option Explicit
logicallinetype_ConditionalCompilation = &H10& ' e.g. #IF #ELSE #END IF
logicallinetype_Comment = &H20& ' Comment line
logicallinetype_ApiDeclaration = &H40& ' e.g. Declare Sleep lib "user32.dll"
logicallinetype_EnumStart = &H80& ' Start of an Enum e.g. Public Enum MyEnum
logicallinetype_EnumMember = &H100& ' elements/members of an Enum. E.g. enum_MyEnum1, enum_MyEnum2
logicallinetype_EnumEnd = &H200& ' End of en enum, e.g. "End Enum"
logicallinetype_TypeStart = &H400& ' Start of a UDT e.g. Public Type MyType
logicallinetype_TypeMember = &H800& ' Members of a UDT
logicallinetype_TypeEnd = &H1000& ' End of a UDT, e.g "End Type"
logicallinetype_ConstantDeclaration = &H2000& ' e.g. Private Const mc_MyConst As Long = 1
logicallinetype_VariableDeclaration = &H4000& ' e.g. Dim X As Long, Private Y As Integer
logicallinetype_MethodStart = &H8000& ' Sub/Function/Property start line
logicallinetype_MethodEnd = &H10000 ' Sub/Function/Property end line
logicallinetype_Label = &H20000 ' Line label
logicallinetype_Statement = &H40000 ' Regular code line/statement
-
Re: VB6 Automated Source Code Processing Helper
Quote:
Originally Posted by
jpbro
Hi jpbro, Sorry, I only downloaded VBPCRE2.dll. I mistakenly thought that pcre2-16.dll is not needed on Win10. After downloading pcre2-16.dll, the project can run normally.
I tested Krool's ComCtlsDemo.vbp with your tool, which took 172 seconds to complete the project analysis. If it's used to analyze my project, it may take a long time, maybe more than 30 minutes(I'll test my project after a few days). If the "Pause" and "Cancel" buttons can be added to the frmProgress window, that would be great.
This tool is very useful, maybe I can apply it to my code editor in the future to check the syntax errors of golang and javascript. I still need to spend some time studying it, especially the use of regular expressions, I have learned several times, but always forget.
Thank you, jpbro. Happy new Year!
-
Re: VB6 Automated Source Code Processing Helper
I made a slight edit to the original post. For a long time now, this site has not allowed the posting of compiled binaries to any place other than the UtilityBank. Obviously that's a significant issue if you are linking to a site like GitHub, because you have no control over what is posted there. For that reason, we are currently allowing links to sites with binaries, as long as that site is GitHub. I removed the one link that wasn't GitHub. After all, anybody from this forum that can't find vbRichClient5 isn't really trying.
-
Re: VB6 Automated Source Code Processing Helper
Quote:
Originally Posted by
dreammanor
Hi jpbro, Sorry, I only downloaded VBPCRE2.dll. I mistakenly thought that pcre2-16.dll is not needed on Win10. After downloading pcre2-16.dll, the project can run normally.
No need to apologize! I wasn't very clear in my original instructions. I've since updated them to let everyone know what files are needed. In any case, I'm glad that's what the problem was, and that it is now resolved.
Quote:
Originally Posted by
dreammanor
I tested Krool's ComCtlsDemo.vbp with your tool, which took 172 seconds to complete the project analysis. If it's used to analyze my project, it may take a long time, maybe more than 30 minutes(I'll test my project after a few days). If the "Pause" and "Cancel" buttons can be added to the frmProgress window, that would be great.
Thanks for the performance notes - if you are using the latest version, it is definitely slower than before because of the new experimental "logical line" parsing. I think I will add an option to disable this, or at least perform that parsing lazily (for example, the first time you attempt to access Logical Line information in a CSourceInfo object, it could do the Logical Line parsing, but not before then). For many use cases, this will help performance significantly. Pause/Cancel are also good ideas, so I will add those too.
Quote:
Originally Posted by
dreammanor
This tool is very useful, maybe I can apply it to my code editor in the future to check the syntax errors of golang and javascript. I still need to spend some time studying it, especially the use of regular expressions, I have learned several times, but always forget.
I hope you will find it useful going forward. Regarding the regexes, one of my planned enhancements is too add a bunch of useful regexes as constants (or perhaps wrapped behind friendly named Sub/Method calls). But if you have anything you are trying to accomplish, let me know and I will try to help. Happy New Year to you too!
-
Re: VB6 Automated Source Code Processing Helper
Just updated the latest source in the first post with some dramatic performance improvements.
First, there's a new option that is enabled by default (optLazyParseLogicalLines). When this is enabled, parsing source files for logical lines will be deferred until the first access of a logical line related property instead of happening as soon as a source file is loaded and split into physical lines. This will dramatically improve the initial loading/populating of the source files list, and since there are currently no Logical Line related properties (since the feature is still experimental) then overall processing will be highly performant.
NOTE: If you want to help me out, please disable the lazy parsing of logical lines option by returning FALSE from optLazyParseLogicalLines in modOptions. This will increase load times but it will help me find errors in my parsing, as I'm sure there are many!
Second, most of the processing time in the example processor was due to inefficient regex scanning of lines in the backward direction. In the example processor, we find a Method start line, then find a matching Method end line, and the look backwards from the end to find an "ON ERROR GOTO <Label>" line. In the original implementation of the RegexMatchLineBackward method, searching would always work backward to the beginning of the file even if we only needed to search back to the Method start line. In projects with lots of lines and no (or few) ON ERROR GOTO <Label> lines, this would result in horrible performance.
I've added an optional StopAfterOneBasedLineNumber parameter to the RegexMatchLineBackward method that we can set to a line number that we know is a logical place to stop looking for matches (e.g. a method starting line number - anything before that line is not relevant). This change results in a significant performance improvement in most cases. On my machine, processing Krool's common controls project took over 100 seconds before the change, and now takes under 3 seconds.
Thanks to @dreammanor for reporting about his experience with the project that resulted in these improvements!
-
Re: VB6 Automated Source Code Processing Helper
Just updated the source in post #1 with a minor bug fix for the misreporting of the # of files on the "Files" tab (cosmetic issue only), and a new "Stats" tab that shows some rudimentary statistics for your project (I plan to expand on the stats reported here in the future).
-
Re: VB6 Automated Source Code Processing Helper
Hi jpbro, sorry for the late reply. I tested your project today. The test results are as follows:
Old VB6SourceProcessor
(1) Process Krool's ComCtlsDemo.vbp: (Project Files:76; Run-Log:11374)
Time1(Select Files): 3.36 seconds;
Time2(Run): 224.57 seconds
(2) Process DreamManor's Large.vbp: (Project Files:567; Run-Log:47647)
Time1(Select Files): 17.88 seconds;
Time2(Run): 361.82 seconds
New VB6SourceProcessor
(1) Process Krool's ComCtlsDemo.vbp: (Project Files:76; Run-Log:11374)
Time1(Select Files): 0.67 seconds;
Time2(Run): 5.86 seconds
(2) Process DreamManor's Large.vbp: (Project Files:567; Run-Log:47647)
Time1(Select Files): 8.26 seconds;
Time2(Run): 39.26 seconds
The performance of the new VB6SourceProcessor is greatly improved. When processing Krool's ComCtlsDemo.vbp, it's almost 40 times faster; when processing my project, it's almost 10 times faster (my project is stored in U-Disk). Very nice, extremely grateful.
In addition, Olaf has demonstrated a more concise and convenient timing test method:
Code:
New_c.Timing True '--- Start timing
'...
'...
'...
MsgBox New_c.Timing '---End timing and display the time spent --
-
Re: VB6 Automated Source Code Processing Helper
Hi Dreammanor, thanks for re-running the tests and reporting the results. I'm glad you are seeing the same significant performance improvements :)
Also re: New_C.Timing, thanks for that. I was aware of it, but I happen to prefer seeing the timing in seconds as opposed to milliseconds, which is why I usually do it the other way. I should probably just roll my own function for the formatting though.
Happy New Year!
-
Re: VB6 Automated Source Code Processing Helper
BIG update to the project (latest source available in the first post). I've added logical line processing to the project, so we can now handle things like line continuations and multiple statements separated by ":" characters on a single line. Performing matches against logical lines is more robust compared to matching against physical lines.
Special thanks to LaVolpe for pointing out that I missed some logical line types (Static methods and Events in particular) and providing some general guidance.
The current list of logical line types that this project detects and parses is as follows:
Code:
Public Enum e_LogicalLineType
[_logicallinetype_AutoDetect] = 0
logicallinetype_Empty = &H1& ' Blank line (empty or whitespace only)
logicallinetype_Header = &H2& ' Stuff in the header section of an FRM file, or "Attributes"
logicallinetype_Attribute = &H4& ' Hidden VB attribute (e.g. ProcedureID)
logicallinetype_Option = &H8& ' e.g. Option Explicit
logicallinetype_ConditionalCompilation = &H10& ' e.g. #IF #ELSE #END IF
logicallinetype_Comment = &H20& ' Comment line
logicallinetype_ApiDeclaration = &H40& ' e.g. Declare Sleep lib "user32.dll"
logicallinetype_EnumStart = &H80& ' Start of Enum e.g. Public Enum MyEnum
logicallinetype_EnumMember = &H100& ' Members/Elements of Enum - enum_MyEnum1, enum_MyEnum2
logicallinetype_EnumEnd = &H200& ' End of Enum, e.g. "End Enum"
logicallinetype_TypeStart = &H400& ' Start of UDT e.g. Public Type MyType
logicallinetype_TypeMember = &H800& ' Elements/Members of a UDT
logicallinetype_TypeEnd = &H1000& ' End of a UDT, e.g "End Type"
logicallinetype_EventDeclaration = &H2000& ' e.g. Public Event MyEvent()
logicallinetype_ConstantDeclaration = &H4000& ' e.g. Private Const mc_MyConst As Long = 1
logicallinetype_VariableDeclaration = &H8000& ' e.g. Dim X As Long, Private Y As Integer
logicallinetype_MethodStart = &H10000 ' Sub/Function/Property start line
logicallinetype_MethodEnd = &H20000 ' Sub/Function/Property end line
logicallinetype_Label = &H40000 ' Line label
logicallinetype_OnErrorGotoLabel
logicallinetype_OnErrorGotoZero
logicallinetype_OnErrorGotoNegativeOne
logicallinetype_OnErrorGotoResumeNext
logicallinetype_Resume ' E.g. Resume Next
logicallinetype_Statement = &H80000 ' Regular code line/statement
' Special "shortcut" enums that combine multiple logical line types
logicallinetype_MultiHidden = logicallinetype_Header Or logicallinetype_Attribute
logicallinetype_MultiVisible = logicallinetype_Option Or logicallinetype_ConditionalCompilation Or logicallinetype_Comment Or logicallinetype_EventDeclaration Or logicallinetype_EnumStart Or logicallinetype_EnumMember Or logicallinetype_EnumEnd Or logicallinetype_TypeStart Or logicallinetype_TypeMember Or logicallinetype_TypeEnd Or logicallinetype_ConstantDeclaration Or logicallinetype_EventDeclaration Or logicallinetype_VariableDeclaration Or logicallinetype_MethodStart Or logicallinetype_MethodEnd Or logicallinetype_Statement
logicallinetype_MultiAll = &HFFFFFFFF
End Enum
Currently logical lines are read-only - so you can run matches against them, but you can't modify them. I will be adding the ability to modify logical lines as soon as I can.
There are a bunch of new methods available on the CSourceInfo class now:
FindNextLogicalLineType - Detect a logical line by type (as in the the enum list above). You can combine logical line enum types to detect the next matching type from a list of types.
FindNextLogicalLineType - Same as above but in the backward looking direction.
RegexMatchLogicalLine - Perform a regex match against a range of logical lines. You can also limit the logical line types to match against by passing an e_LogicalLineType enum (or combination of enums for multiple line type matches).
LogicalLineCount - Get a count of the number of logical lines in a module.
LogicalLineContent - Get the content of a specific logical line.
LogicalLineTypeInRange - Return TRUE if a passed logical line type enum is present within a range of lines.
MethodNameFromLogicalLine - Returns the name of the method to which the passed logical line number belongs.
That's it for now - there's almost guaranteed to be problems with the logical line parsing and I would love to get examples from anyone who runs into problems so I can fix them.
Enjoy :)
-
Re: VB6 Automated Source Code Processing Helper
I should note that the Logical Line processing will slow things down a bit again, although it will produce more correct results over a wider variety of coding styles.
Processing Krool's project against logical lines now takes about 80 seconds on my machine. Still decent considering all the extra benefits we will get from making logical line processing available.
That said, you can still use the older/faster physical line processing methods if you don't need to consider things like line continuations & multiple statements per line that have been separated by a ":" for you processing needs. By default Logical Lines won't be parsed unless you call a logical line method, so there's no performance hit against the older physical line methods if the logical line methods aren't used.
-
Re: VB6 Automated Source Code Processing Helper
jpbro, couple of observations
You may be familiar with this reference, if not here it is. Describes in wonky terms how VB interprets stuff like physical and logical lines and much more. https://msdn.microsoft.com/en-us/library/ee199815.aspx
This isn't meant to nit picky and I don't think I fully understand your RegEx expressions to say what I'm about to jot down isn't already handled.
1. Attempting to find 2+ words in a single InStr() call can fail. This is because VB allows continuations in just about every way you can imagine with only a few exceptions: no continuations for enum members and obviously cannot split a "word/token". So if looking for something like "end type", "property get" can fail because those words may be split on different lines
2. Think you are missing "Implements" statement detection, if you intended to trap these too.
3. Variables in a declarations section can also be preceded with Public and Global. I see that you are testing for Dim, Private & Static
4. DefType statements? https://docs.microsoft.com/en-us/pre...421(v%3dvs.60)
5. Don't know any modern coder that uses this, but another type of comment block uses the keyword "Rem". When used, it must start a statement. Can't be appended to a normal statement.
i.e., this is invalid: X = Y Rem Some Comments
but this is valid: X = Y: Rem Some Comments
6. Don't know if you want to address these: On [expression] GoTo labelA, labelB, etc and/or On [expression] GoSub labelC, labelD, etc and/or Gosub. If you do handle GoSub calls, might want to treat them like a block also, since the "sub" needs a "Return" statement.
https://docs.microsoft.com/en-us/pre...175(v%3dvs.60)
https://docs.microsoft.com/en-us/pre...378(v%3dvs.60)
7. Just a reminder: "With" statements. Briefly talked about it before
A coder's fun project is never finished, is it?
-
Re: VB6 Automated Source Code Processing Helper
Hi LaVolpe,
First of all, thank you very much for taking the time to look at the project and provide criticism. It is very appreciated, and don't worry about being nit-picky with me, I'm happy to have my nits picked ;)
[QUOTE=LaVolpe;5349033]You may be familiar with this reference, if not here it is. Describes in wonky terms how VB interprets stuff like physical and logical lines and much more. https://msdn.microsoft.com/en-us/library/ee199815.aspx
I was not aware of that reference, thanks for providing it. I somewhat shamefully admit that my attempts at parsing/interpreting VB6 source is based almost entirely on the way I code. I also somewhat shamefully admit that my style of coding is not based on any formal education, just many years of hacking away, so my codebase is definitely an inadequate sample to base a parser/interpreter on alone. I think I should have been clearer that this project is a work in progress, so I will make a note of that in the first post.
As an aside - my lack of formal education and the fact that I program in different languages from time to time has probably led to some "bad" VB6 nomenclature on my part. Things like my use of the word "method" vs. "procedure" and some other language I've used is probably incorrect or confusing. This project was originally developed in a more basic form some years ago just to help ensure some QA with my own code before compilation/distrubution, so there will be some growing pains as I make it more generic and useful for all.
Quote:
Originally Posted by
LaVolpe
I don't think I fully understand your RegEx expressions to say what I'm about to jot down isn't already handled.
Based on your notes below, I think you've understood them enough. There's the old quote about Regexes that I like:
Quote:
Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems.
But for whatever reason I have a fondness for regexes...probably related to some mental pathology that keeps me writing new software in VB6 at this late date ;)
Quote:
Originally Posted by
LaVolpe
1. Attempting to find 2+ words in a single InStr() call can fail. This is because VB allows continuations in just about every way you can imagine with only a few exceptions: no continuations for enum members and obviously cannot split a "word/token". So if looking for something like "end type", "property get" can fail because those words may be split on different lines
I'll review my code, but I thought I was only passing Logical Lines as parsed and normalized units that couldn't fail like this. I may be wrong about that though, so I will double-check all my tests.
Quote:
Originally Posted by
LaVolpe
2. Think you are missing "Implements" statement detection, if you intended to trap these too.
You are 100% correct - those would be interpreted as just "Statements" but I agree I should have a logical line type for "Implements" lines, thank you :)
Quote:
Originally Posted by
LaVolpe
3. Variables in a declarations section can also be preceded with Public and Global. I see that you are testing for Dim, Private & Static
Well that's a "duh" on my part (especially missing "Public"....I don't use "Global" myself, but missing "Public" was just a flub). Thanks again!
Quote:
Originally Posted by
LaVolpe
Definitely an omission on my part, I've seen them but forgot they existed. Thanks again! Maybe I should save the thanks to the end ;)
Quote:
Originally Posted by
LaVolpe
5. Don't know any modern coder that uses this, but another type of comment block uses the keyword "Rem". When used, it must start a statement. Can't be appended to a normal statement.
i.e., this is invalid: X = Y Rem Some Comments
but this is valid: X = Y: Rem Some Comments
Totally forgot about "REM" statements! I haven't used them since my Commodore 64 days :)
Quote:
Originally Posted by
LaVolpe
GoSubs are another one I forgot about because I don't use them, but thanks for the reminder.
Quote:
Originally Posted by
LaVolpe
7. Just a reminder: "With" statements. Briefly talked about it before
That's yet another "duh" on my part...I use "With" blocks all the time and just forgot to implement tests for them.
Looks like it is time to get back to work!
Quote:
Originally Posted by
LaVolpe
A coder's fun project is never finished, is it?
Now that's the truth! But I wouldn't have it any other way...I caught the programming "bug" when I was 5 and was typing out "Compute!" magazine programs on my VIC 20 (with the help of my Mom who did most of the typing while I recited the lines) and I've been working on "unfinished" projects ever since. Maybe that's one of the reasons that Jeff Atwood's concept of The Infinite Version resonates with me.
Anyway, I would like to extend a final proper "Thank you very much!" for taking the time to take a look at my project and provide such valuable feedback! It's truly a privilege to get advice from VB6 Masters such as yourself.
-
Re: VB6 Automated Source Code Processing Helper
You're welcome, glad I could pick your nits -- jeez that sounds so wrong; maybe my attempt at humor stops there.
Compute! Mag? Mine was PC Computing. Loved playing with those DOS functions found towards end of magazine. Those required you to type hex programs using EdLin to create .com files directly from the DOS prompt. Hmmm, or was that Compute! magazine? I had subscriptions for both.
-
Re: VB6 Automated Source Code Processing Helper
Quote:
Originally Posted by
LaVolpe
You're welcome, glad I could pick your nits -- jeez that sounds so wrong; maybe my attempt at humor stops there.
LOL, agreed that that line of humour might be done ;)
Quote:
Originally Posted by
LaVolpe
Compute! Mag? Mine was PC Computing. Loved playing with those DOS functions found towards end of magazine. Those required you to type hex programs using EdLin to create .com files directly from the DOS prompt. Hmmm, or was that Compute! magazine? I had subscriptions for both.
Now that I think of it, I actually had "Compute!'s Gazette", which was the Commodore version of Compute!. Probably the same format, just with different focus. the VIC20 and Commode 64 both booted up to BASIC, so all of the code in those magazines was BASIC code - normally with a tonne of "DATA" statements followed by "random" numbers (to a young brain they seemed random at least - that part always seemed like magic). Getting access to .RES files sure beat DATA statements. PS: I just checked, and VB6 doesn't support DATA statements ;) I came late to the PC actually, I think I got my first PC around 1997 or so. I stuck with Commodore long past any rational point (VIC20 > C64/128 > Amiga).
Cheers to those old systems that got us here!
PS: I'm almost done some changes to my code to address your comments, thanks again for taking the time to provide them!
-
Re: VB6 Automated Source Code Processing Helper
Well that's a bummer - taking into account all the additional Logical Line types, I'm coming to 33 types. I've been using bitwise comparisons in order to allow for combining logical line types for matches, limiting me to 32 enums. I'm going to have to rethink this a bit.
-
Re: VB6 Automated Source Code Processing Helper
Hi jpbro, your project is great. After I finish my work, I'll study and test it carefully.
-
Re: VB6 Automated Source Code Processing Helper
Quote:
Originally Posted by
jpbro
Well that's a bummer - taking into account all the additional Logical Line types, I'm coming to 33 types. I've been using bitwise comparisons in order to allow for combining logical line types for matches, limiting me to 32 enums. I'm going to have to rethink this a bit.
Well, you have a few you've designated for "ends", i.e., type end, enum end, etc
You can use just one bit for a generic "end block" , example:
Code:
logicallinetype_EndBlock = &H80000000
logicallinetype_EnumStart = &H80&
logicallinetype_EnumMember = &H100&
logicallinetype_EnumEnd = logicallinetype_EnumStart Or logicallinetype_EndBlock
logicallinetype_TypeStart = &H400&
logicallinetype_TypeMember = &H800& ' Elements/Members of a UDT
logicallinetype_TypeEnd = logicallinetype_TypeStart Or logicallinetype_EndBlock
logicallinetype_MethodStart = &H10000 ' Sub/Function/Property start line
logicallinetype_MethodEnd = logicallinetype_MethodStart Or logicallinetype_EndBlock
likewise, you can free up another bit by using just one bit for both the enum member & type member, similar logic as above, i.e., logicallinetype_TypeMember = logicallinetype_TypeStart Or logicallinetype_Member
Edited: Above said, do you need to OR any of the line types to begin with? In other words, would you be using values like logicallinetype_TypeStart Or logicallinetype_EnumStart or other combinations? If not, then you have 2 billion possibilities left: just apply values 1-n vs. bits.
In my projects, sometimes I'll do both: I may have a block of enum values that will be OR'd together and a block that will never be, but want to keep them in the same Enumeration. So, I'll separate the blocks so that neither will overlap each other, i.e.,
Code:
Enum Whatever
member_notOr1 = 1 ' start not-OR values: this block of numbers will never be OR'd with each other
member_notOr2
member_notOr3
member_notOr4
... etc
member_canOr1 = &H100 ' start OR values: 0 to 255 are reserved for "not Or" values
member_canOr2 = &H200 ' this block can be OR'd with each other and/or the previous block if needed
member_canOr3 = &H400
... etc
End Enum
-
Re: VB6 Automated Source Code Processing Helper
[QUOTE=LaVolpe;5349921]Well, you have a few you've designated for "ends", i.e., type end, enum end, etc
You can use just one bit for a generic "end block" , example:
...
likewise, you can free up another bit by using just one bit for both the enum member & type member, similar logic as above, i.e., logicallinetype_TypeMember = logicallinetype_TypeStart Or logicallinetype_Member
That was smart, thanks for that tip :)
Quote:
Originally Posted by
LaVolpe
Edited: Above said, do you need to OR any of the line types to begin with? In other words, would you be using values like logicallinetype_TypeStart Or logicallinetype_EnumStart or other combinations? If not, then you have 2 billion possibilities left: just apply values 1-n vs. bits.
I did want to keep it as an option to OR line types together for matching purposes. For example, you could do a regex match on just logicallinetye_TypeStart Or logicallinetype_EnumStart only to make sure that some kind of naming convention is adhered to. It saves time on the matching because the recordset to match against will only include TypeStart and EnumStart lines. Now it might be rare for this to happen, but I think there are cases where it can be useful.
Quote:
Originally Posted by
LaVolpe
In my projects, sometimes I'll do both: I may have a block of enum values that will be OR'd together and a block that will never be, but want to keep them in the same Enumeration. So, I'll separate the blocks so that neither will overlap each other, i.e.,
Code:
Enum Whatever
member_notOr1 = 1 ' start not-OR values: this block of numbers will never be OR'd with each other
member_notOr2
member_notOr3
member_notOr4
... etc
member_canOr1 = &H100 ' start OR values: 0 to 255 are reserved for "not Or" values
member_canOr2 = &H200 ' this block can be OR'd with each other and/or the previous block if needed
member_canOr3 = &H400
... etc
End Enum
That's a smart approach too, I'll have to give some more thought as to whether I can split up my enums into ORable/non-ORable blocks. Thanks again for all the great advice!
-
Re: VB6 Automated Source Code Processing Helper
Quote:
Originally Posted by
dreammanor
Hi jpbro, your project is great. After I finish my work, I'll study and test it carefully.
Thanks for the kind words dreammanor - I hope you eventually find the project useful, and I'm looking forward to getting feedback from you as you try it out.
-
Re: VB6 Automated Source Code Processing Helper
Quote:
Originally Posted by
jpbro
Thanks for the kind words dreammanor - I hope you eventually find the project useful, and I'm looking forward to getting feedback from you as you try it out.
I have a very accurate feeling about the technology I need. When I first saw VBFastCGI, I knew that it was what I had been searching for for a long time, but I only started using it a year later. Your VB6SourceProcessor is exactly what I need, but I need to wait until my current project is done before I start using it. Thank you, jpbro. Your product always has a unique idea.
-
Re: VB6 Automated Source Code Processing Helper
Hi, jpbro, I'm testing your VBPCRE2. @Dragokas said in a thread PCRE is a most powerfull and fastest regular expressions engine, but on my computer, VBPCRE2 seems to be much slower than VBScipt.RegExp. Below are the test code and test results:
VBScript.RegExp: 390.84msec
VBPCRE2: 1075.10msec
Code:
Public Sub Test_RE_VBScript()
Dim RE As VBScript_RegExp_55.RegExp
Dim M As VBScript_RegExp_55.Match
Dim MC As VBScript_RegExp_55.MatchCollection
Dim sText As String
Dim i As Long
'--- Hello你好 ---
Const TEST_STRING = "&#" & "72" & ";" & "&#" & "101" & ";" & "&#" & "108" & ";" & "&#" & "108" & ";" & "&#" & "111" & ";" & "&#" & "20320" & ";" & "&#" & "22909" & ";"
Set RE = CreateObject("VBScript.RegExp")
New_c.Timing True
For i = 1 To 10000
sText = TEST_STRING '--- Hello你好 ---
RE.Pattern = "&#([0-9]+);"
Do
Set MC = RE.Execute(sText)
For Each M In MC
If M.SubMatches.Count Then
sText = Replace$(sText, M.Value, ChrW$(M.SubMatches(0))) ' Replace the match with the CHRW$ the numeric submatch of the match
End If
Next M
Loop While MC.Count ' Repeat this loop for each match
Next i
MsgBox New_c.Timing
Debug.Print sText
Set RE = Nothing
End Sub
Public Sub Test_RE_Jpbro()
Dim RE As VBPCRE2.cPcre2
Dim M As cPcre2Match
Dim MC As VBPCRE2.cPcre2Matches
Dim sText As String
Dim i As Long
'--- Hello你好 ---
Const TEST_STRING = "&#" & "72" & ";" & "&#" & "101" & ";" & "&#" & "108" & ";" & "&#" & "108" & ";" & "&#" & "111" & ";" & "&#" & "20320" & ";" & "&#" & "22909" & ";"
Set RE = New VBPCRE2.cPcre2
New_c.Timing True
For i = 1 To 10000
sText = TEST_STRING '--- Hello你好 ---
RE.Pattern = "&#([0-9]+);"
Do
Set MC = RE.Execute(sText)
For Each M In MC
If M.SubMatchCount Then
sText = Replace$(sText, M.MatchedText, ChrW$(M.SubMatchValue(0))) ' Replace the match with the CHRW$ the numeric submatch of the match
End If
Next M
Loop While MC.Count ' Repeat this loop for each match
Next i
MsgBox New_c.Timing
Debug.Print sText
Set RE = Nothing
End Sub
-
Re: VB6 Automated Source Code Processing Helper
FWIW (since the topic of RegExpression within JS came up) - here my comparison to the VB-RegExp-Object,
using cActiveScript with "JScript9" (and 3 RegEx-related, generic Helper-Functions - based on your example above):
Code:
Option Explicit
Private WithEvents SC As cActiveScript, CO As Object
Private Sub Form_Load()
Set SC = New_c.ActiveScript("JScript9", False, False)
SC.AddCode "function RegExReplace(s, sPat, sRepl){" & vbCrLf & _
" var re = new RegExp(sPat, 'g')" & vbCrLf & _
" return s.replace(re, sRepl)" & vbCrLf & _
"}"
SC.AddCode "function RegExReplaceEval(s, sPat, sEval){" & vbCrLf & _
" var re = new RegExp(sPat, 'g')" & vbCrLf & _
" eval('function f(match,$1,$2,$3){return '+sEval+'}')" & vbCrLf & _
" return s.replace(re, f)" & vbCrLf & _
"}"
SC.AddCode "function RegExMatch(s, sPat, sJoin){" & vbCrLf & _
" var re = new RegExp(sPat, 'g')" & vbCrLf & _
" return s.match(re).join(sJoin)" & vbCrLf & _
"}"
Set CO = SC.CodeObject 'calls work fastest, when we use the CodeObject
End Sub
'convenience-wrappers around (latebound) calls of the CodeObject
Private Function RegExReplace(S As String, sPat As String, sRepl As String) As String
RegExReplace = CO.RegExReplace(S, sPat, sRepl)
End Function
Private Function RegExReplaceEval(S As String, sPat As String, sEval As String) As String
RegExReplaceEval = CO.RegExReplaceEval(S, sPat, sEval)
End Function
Function RegExMatch(S As String, sPat As String, Optional sJoin As String = ",") As String
RegExMatch = CO.RegExMatch(S, sPat, sJoin)
End Function
Private Sub Form_Click()
Test_RE_JS1
Test_RE_JS2
Test_RE_VBScript
End Sub
Public Sub Test_RE_JS1()
Const TEST_STRING = "&#" & "72" & ";" & "&#" & "101" & ";" & "&#" & "108" & ";" & "&#" & "108" & ";" & "&#" & "111" & ";" & "&#" & "20320" & ";" & "&#" & "22909" & ";"
Const Pat$ = "&#([0-9]+);"
New_c.Timing True
Dim i As Long, sText As String
For i = 1 To 10000
sText = TEST_STRING
sText = RegExReplaceEval(sText, Pat, "String.fromCharCode($1)")
Next
Debug.Print "JScript9-1", New_c.Timing, sText
End Sub
Public Sub Test_RE_JS2()
Const TEST_STRING = "&#" & "72" & ";" & "&#" & "101" & ";" & "&#" & "108" & ";" & "&#" & "108" & ";" & "&#" & "111" & ";" & "&#" & "20320" & ";" & "&#" & "22909" & ";"
Const Pat$ = "&#([0-9]+);"
New_c.Timing True
Dim i As Long, sText As String, Match
For i = 1 To 10000
sText = TEST_STRING
For Each Match In Split(RegExMatch(sText, Pat, "|"), "|")
sText = Replace$(sText, Match, ChrW$(Val(Mid$(Match, 3))))
Next
Next
Debug.Print "JScript9-2", New_c.Timing, sText
End Sub
Public Sub Test_RE_VBScript()
Dim RE As VBScript_RegExp_55.RegExp
Dim M As VBScript_RegExp_55.Match
Dim MC As VBScript_RegExp_55.MatchCollection
Dim sText As String
Dim i As Long
Const TEST_STRING = "&#" & "72" & ";" & "&#" & "101" & ";" & "&#" & "108" & ";" & "&#" & "108" & ";" & "&#" & "111" & ";" & "&#" & "20320" & ";" & "&#" & "22909" & ";"
Set RE = New VBScript_RegExp_55.RegExp
New_c.Timing True
For i = 1 To 10000
sText = TEST_STRING '--- Hello?? ---
RE.Pattern = "&#([0-9]+);"
Do
Set MC = RE.Execute(sText)
For Each M In MC
If M.SubMatches.Count Then
sText = Replace$(sText, M.Value, ChrW$(M.SubMatches(0))) ' Replace the match with the CHRW$ the numeric submatch of the match
End If
Next M
Loop While MC.Count ' Repeat this loop for each match
Next i
Debug.Print "RexExpVB", New_c.Timing, sText
End Sub
On my machine, Test_RE_JS1 is (with about 100msec) about 3 times as fast as Test_RE_VBScript...
HTH
Olaf
-
Re: VB6 Automated Source Code Processing Helper
Hi Olaf. I tested your code, and Test_RE_JS1 is indeed 3 times as fast as Test_RE_VBScript, which is indeed an exciting thing. Thank you very much.
-
Re: VB6 Automated Source Code Processing Helper
Hi, jpbro. I need to use a lot of regular expressions when parsing html, css and JavaScript. To be consistent with Chrome and FireFox's regular expressions, I still use JScript for my regular expressions in most cases.
Currently, neither VBScript nor JScript9 seems to provide the unicode pattern "/u". For example: "/[a-z]/u" is not recognized by VBScript and JScript.
I'd like to know if VBPCRE2 supports unicode pattern "/u". If it does, maybe I should try a solution that mix VBPCRE2 with JScript, that is, when there is no "/u", I'll still use JScript, if there is a "/u" tag, I will use VBPCRE2 to handle regular expression. Hope to get your help, thanks.
-
Re: VB6 Automated Source Code Processing Helper
Do you mean "\u" or "/u"? See this snippet from the pcre2syntax man page on "\u"
Code:
If PCRE2_ALT_BSUX or PCRE2_EXTRA_ALT_BSUX is set ("ALT_BSUX mode"), the following are also recognized:
\U the character "U"
\uhhhh character with hex code hhhh
\u{hh..} character with hex code hh.. but only for EXTRA_ALT_BSUX
Can you provide me a small snippet of text, a regex pattern using \u or /u and what you are expecting to see matched? I can run some tests with PCRE2 and let you know what I find.
-
Re: VB6 Automated Source Code Processing Helper
I mean "/u", for example:
Code:
/^\uD83D/u.test('\uD83D\uDC2A')
// false
/^\uD83D/.test('\uD83D\uDC2A')
// true
OR
Code:
var regex = new RegExp('\u{61}', 'u')
OR
Code:
let str = "A ბ ㄱ";
alert( str.match(/\p{L}/gu) ); // A,ბ,ㄱ
OR
Code:
let regexp = /\p{sc=Han}/gu; // returns Chinese hieroglyphs
let str = `Hello Привет 你好 123_456`;
alert( str.match(regexp) ); // 你,好
https://developer.mozilla.org/en-US/...RegExp/unicode
https://javascript.info/regexp-unicode
-
Re: VB6 Automated Source Code Processing Helper
So /u is a flag for unicode support, which VBPCRE2 can handle by setting the compile options .UTF8 property to False. In order to handle \uXXXX notation, we also need to set the compile options .AlternateBsuxHandling property to True.
Code:
Set lo_RegEx2 = New cPcre2
With lo_RegEx2.Options.Compile
.Utf = False
.AlternateBsuxHandling = True
End With
Set lo_Matches2 = lo_RegEx2.Execute(ChrW$(&HD83D) & ChrW$(&HDC2A), "^\uD83D")
Debug.Print "Match Count: " & lo_Matches2.Count
At least I think that works as required, let me know if there are any problems with the above.
-
Re: VB6 Automated Source Code Processing Helper
OK, I'll do further testing. Thank you very much, jpbro.
-
Re: VB6 Automated Source Code Processing Helper
Only 3 years later, an update ;)
- Updated to latest version of Kr00l's components and Olaf's RC6.
- You can now filter the log by row type (Info, Important, Warning, Error)
- You can now cancel processing mid-way through.
- Added File X of Y progress label so you can tell how far along the process is.
- Added Lines/Second calculation to make it easier to see when I've been able to improve performance.
- Fixed statistics calculations and a handful of small bugs.
-
Re: VB6 Automated Source Code Processing Helper
If you can provide the complete source code, don't use other DLLs.
If you can implement all the lexical analysis of the source code, and even add new syntax, that would be perfect.
If you use multithreading, the speed should be several times faster, right? For example, a CPU with 6 cores uses 12 multithreads for processing.
-
Re: VB6 Automated Source Code Processing Helper
Quote:
Originally Posted by
jpbro
Only 3 years later, an update ;)
- Updated to latest version of Kr00l's components and Olaf's RC6.
- You can now filter the log by row type (Info, Important, Warning, Error)
- You can now cancel processing mid-way through.
- Added File X of Y progress label so you can tell how far along the process is.
- Added Lines/Second calculation to make it easier to see when I've been able to improve performance.
- Fixed statistics calculations and a handful of small bugs.
Oh, I almost missed this excellent tool, and I completely forgot that I had tested it carefully. My memory seemed to be similar to that of a fish.
I didn't have the opportunity to use it a few years ago, and now I may have the opportunity to use it fully and in depth, thank you very much, jpbro.
-
Re: VB6 Automated Source Code Processing Helper
Quote:
Originally Posted by
SearchingDataOnly
Oh, I almost missed this excellent tool, and I completely forgot that I had tested it carefully. My memory seemed to be similar to that of a fish.
I didn't have the opportunity to use it a few years ago, and now I may have the opportunity to use it fully and in depth, thank you very much, jpbro.
Thanks @SDO - it's still very much a work in progress, so make sure you have backups before you use it on your important stuff!
Also, you might want to wait an hour or two before you spend some time with it, I have another update coming shortly.
-
Re: VB6 Automated Source Code Processing Helper
I just updated the source code in the first post.
I primarily focused on performance improvements for this release, so the biggest feature of the latest version is that it is significantly more efficient. On my machine, the example processor now takes about 4 minutes for a project with almost 1000 source files and approximately 775,000 lines. The previous version took about 14 minutes, so not bad! This was achieved through a few means:
- I removed the dependency on the class based VBPCRE2.dll PCRE2 wrapper, and I am now using a minimal straight to pcre2-16.dll .BAS wrapper.
- I swapped out some RC6.cArrayList use for plain-old VB6 String Arrays. The cArrayList was overkill because I'm not inserting/adding/removing elements which are activities where the cArrayList really shines.
- I took some logic out of the VB6 code for finding lines and moved it into SQL statements (and added some hopefully useful indexes for the queries to work against, though I haven't had time to check the query plans).
- Enabled all the usual compiler optimizations.
- Mapped the source code string data to in integer SafeArray. Loops/tests are against the array which avoids a lot of string operations/comparisons.
I also fixed a few more bugs and added some other niceties:
- Better progress and elapsed time indicators on the Progress window.
- Added total source lines count the Stats tab.
- The Log tab now auto updates to show the # of rows as log level types are toggled on/off.
- I've added a System folder where you should drop pcre2-16.dll. VBPCRE2.dll is no longer required.
I think that's everything for now, enjoy :)
-
Re: VB6 Automated Source Code Processing Helper
vbflexgrid
VbPcre2
What automated code downloads multiple projects from open source websites or multiple download addresses, and then compiles them into multiple DLLs.
-
Re: VB6 Automated Source Code Processing Helper
The primary goal of this project is to have a comprehensive wrapper for PCRE2 in an ActiveX DLL for use in VB6 or other COM supporting languages.
The secondary goal of this project is to be a drop-in replacement for the VBSscript RegExp object.
Can also use the regular expression of JS? Which of these three methods is faster? It's more convenient.
-
Re: VB6 Automated Source Code Processing Helper
Quote:
Originally Posted by
xiaoyao
If you can provide the complete source code, don't use other DLLs.
Sorry, but my stuff probably isn't going to be of interest to you if you don't want to use other DLLs/OCXs. I almost always use at least RC5/RC6 in the stuff I publish, because it saves a lot of time and hassle, and I don't mind having the dependency.
Quote:
Originally Posted by
xiaoyao
If you can implement all the lexical analysis of the source code, and even add new syntax, that would be perfect.
That's going way beyond the scope of the goal of this project. It's only intended to give you access to the logical lines of your source (with a bit of extra metadata to help you determine the type of line) so you can make pattern based substitutions, test for things that don't meet your coding standards, automatically insert boilerplate code, etc... So it's "dumb" on purposes - the brains are the ISourceProcessor implementing classes that you write yourself to do whatever you want with the code.
Quote:
Originally Posted by
xiaoyao
If you use multithreading, the speed should be several times faster, right? For example, a CPU with 6 cores uses 12 multithreads for processing.
Multithreading is a possibility that I might look into...it adds some complexity, and I'm not sure whether the overhead will guarantee a massive win, but it might be worth a try.
-
Re: VB6 Automated Source Code Processing Helper
Quote:
Originally Posted by
xiaoyao
The primary goal of this project is to have a comprehensive wrapper for PCRE2 in an ActiveX DLL for use in VB6 or other COM supporting languages.
The secondary goal of this project is to be a drop-in replacement for the VBSscript RegExp object.
Can also use the regular expression of JS? Which of these three methods is faster? It's more convenient.
There's a thread comparing the speed of various regex engines here starting here: https://www.vbforums.com/showthread....=1#post5444643
VBPCRE2 does not perform very well - I need take a pass at optimizing it, but I haven't had the time/inclination. Instead, the minimal PCRE2 (.BAS modules only, no Classes) performs much better: https://www.vbforums.com/showthread....=1#post5444745
That said, it might depend on your workload which engine does the best (although it looks like @wqweto's vbPeg (introduced in this post) kicks some serious "you know what".
-
Re: VB6 Automated Source Code Processing Helper
VbPcre2,There is no problem with this source code. At first, I thought I needed to download something I didn't know.
In the past, I downloaded web pages to collect commodity information in my own way, and the code often needs to be modified. If I use real expressions, I just need to make a regular expression formula table.
It would also be convenient if you could parse VB6 project files with regular expressions. There are also control properties for the form file.
Every function, every procedure, every program code can be parsed with regular expressions, which is also very convenient.
-
Re: VB6 Automated Source Code Processing Helper
Quote:
Originally Posted by
jpbro
Thanks @SDO - it's still very much a work in progress, so make sure you have backups before you use it on your important stuff!
Also, you might want to wait an hour or two before you spend some time with it, I have another update coming shortly.
Quote:
Originally Posted by
jpbro
I just updated the source code in the first post.
I primarily focused on performance improvements for this release, so the biggest feature of the latest version is that it is significantly more efficient. On my machine, the example processor now takes about 4 minutes for a project with almost 1000 source files and approximately 775,000 lines. The previous version took about 14 minutes, so not bad! This was achieved through a few means:
- I removed the dependency on the class based VBPCRE2.dll PCRE2 wrapper, and I am now using a minimal straight to pcre2-16.dll .BAS wrapper.
- I swapped out some RC6.cArrayList use for plain-old VB6 String Arrays. The cArrayList was overkill because I'm not inserting/adding/removing elements which are activities where the cArrayList really shines.
- I took some logic out of the VB6 code for finding lines and moved it into SQL statements (and added some hopefully useful indexes for the queries to work against, though I haven't had time to check the query plans).
- Enabled all the usual compiler optimizations.
- Mapped the source code string data to in integer SafeArray. Loops/tests are against the array which avoids a lot of string operations/comparisons.
I also fixed a few more bugs and added some other niceties:
- Better progress and elapsed time indicators on the Progress window.
- Added total source lines count the Stats tab.
- The Log tab now auto updates to show the # of rows as log level types are toggled on/off.
- I've added a System folder where you should drop pcre2-16.dll. VBPCRE2.dll is no longer required.
I think that's everything for now, enjoy :)
I tested VB6SourceProcessor4 and it worked very well. I scanned my projects with it and it checked out that one of my cls files was stored in the wrong path, which was great.
A few months ago, an important file in one of my projects was stored in the wrong path (c:\windows\system), and when I reinstalled windows, the file was lost.
Now, VB6SourceProcessor4 has helped me avoid similar pitfalls.
Also, the day before yesterday, I found a small bug in VB6SourceProcessor2, and I was about to report it, but this bug no longer exists in VB6SourceProcessor4.
You shared another outstanding tool, just like you did before. I believe VB6SourceProcessor will be of great help to my new project (a project analysis tool), thank you, jpbro.
-
Re: VB6 Automated Source Code Processing Helper
Quote:
Originally Posted by
jpbro
WORK IN PROGRESS
This is a work in progress - it does a pretty good job of doing what it is trying to do, but there are definitely holes in the implementation. If you can provide any source code that fails, I will be happy to fix my code to accommodate it!
WHAT IS THIS?
This project parses VBG & VBP files, building a list of all source code files (with limitations, see the KNOWN/EXPECTED PROBLEMS section below). You can then loop through each source file, and each line of code therein to perform just about any line-level custom processing you can imagine. The project includes fast PCRE2 regex support for finding matching lines of code (in forward and reverse directions).
When you find a line you are looking for, you can optionally replace the text with something else, and when you are done you can save the file back to disk.
... .. .
...
..
.
Hope some of you find this project useful!
Dear JpBro,
First of all, very many thanks for conceptualising a project of this sort. Of course, Thanks a TON for the project itself.
Actually, I came here by chance. How I came here is as follows:
1. Recently, I started using VbScript RegEx as UDF in Sql queries (All thanks, as ever, to great Olaf; https://www.vbforums.com/showthread....=1#post5588333)
2. When I wished to use lookbehinds, I understood that vbr (Vbscript Regex) would not support the same. I immediately remembered your vbpcre2 which I had used some time ago in a project. So, I started using it as an UDF in Sql queries (thanks again to great Olaf for guiding me on the same) but then I found it too slow when compared to vbr.
3. Before reporting the same in the vbpcre2 thread, I thought I will make some explorations at my end so that I can share my findings in case my observation that pcre2 is slower than vbr was wrong.
4. Thereafter, before reporting my observation, I thought I will better search in the net on "vb6 pcre2 is slower than regex" and see what it says.
5. The above search listed this thread as the first page and that's how I am here.
6. Well, now that I know (after quickly going through some posts of this thread) that vbpcre2 is indeed slower than vbr, I have a few questions:
--
a. You have written in one of the posts that you started accessing pcre2-16.dll directly instead of from the class-based wrapper (vbpcre2.dll). Is it possible for me also to do the same? If so, how?
b. By accessing pcre2-16.dll directly, did it increase the speed of processing to be as fast as vbr at least, if not faster? Or, is it still slower?
c. Any plans of upgrading vbpcre2 to the latest pcre2 version?
d. You have written that you are not getting time to work on increasing the speed of the existing vbpcre2.dll. I sincerely prayed just now that you do get the time - either for increasing the speed of vbpcre2.dll OR for increasing the speed of processing of your direct accessing of pcre2-16.dll so that the speed is at least as fast as vbr and if possible faster than vbr itself. That would be a fantastic big big big boon to the society. I prayed well for Olaf to get time too, somehow, to join hands with you (much the same way Tanner was able to do, years back) and succeed in increasing the speed of either vbpcre2 or 'the process of direct accessing of pcre2-16.dll' to be much much faster than VBScript Regex.
--
Coming back to your parser project (which is what this thread is all about), I am sure finding out methods not having "On error goto" at the top will be very useful for me (since so far I have been doing it manually only). Once I download the project and get time to start exploring it (I have noted your warnings well), I am sure I will find many more uses. As for suggestions (as asked by you), I am sure there is a tool/feature already existing, neatly tabulating the number of lines in each .frm, .bas, .cls, etc. and the total number of lines under each of these heads (frm, cls, bas, etc.) and finally in the whole project itself. As I explore, I will get to know anyway, whether this feature exists or not. Nevertheless, since I am here now writing, just thought of quickly letting you know this need, in case it is not already existing. Thanks a TON, once again.
God Bless you, jpbro and tanner. God Bless all.
Happy Christmas week! :). And, a very Happy New year! :)
Kind Regards.
-
Re: VB6 Automated Source Code Processing Helper
Quote:
Originally Posted by
softv
First of all, very many thanks for conceptualising a project of this sort. Of course, Thanks a TON for the project itself.
Thank you for the kind words. This project is very rough, and I don't have much time to improve it so don't thank me too much yet ;)
Quote:
Originally Posted by
softv
Actually, I came here by chance. How I came here is as follows:
Thank you also for the detailed steps on how you got here, it gives me some good context after being away from the project for so long. Maybe I'm just an LLM after all!
Quote:
Originally Posted by
softv
a. You have written in one of the posts that you started accessing pcre2-16.dll directly instead of from the class-based wrapper (vbpcre2.dll). Is it possible for me also to do the same? If so, how?
Yup! Just add the *Pcre.bas and *Regex.bas modules to your project and you can work against it.
Just dump VbPrce.dll - it's ancient code and basically junk.
Quote:
Originally Posted by
softv
b. By accessing pcre2-16.dll directly, did it increase the speed of processing to be as fast as vbr at least, if not faster? Or, is it still slower?
It increased speed dramatically, but I didn't benchmark it against anything else (feel free to run and post benchmarks if you care to). That said, based on my past results in performance teste after some optimization passes, I do ok but if you want peak performance you should look elsewhere.
Quote:
Originally Posted by
softv
c. Any plans of upgrading vbpcre2 to the latest pcre2 version?
0% plans at this point unless you have a compelling reason?
I hope that helps a bit, and I appreciate the rest of your post, but it will take more time than I have to respond properly right now. Happy New Year to you too!
-
Re: VB6 Automated Source Code Processing Helper
Thanks a lot for your kind replies, JpBro, even amidst your hectic schedules.
I indeed wanted to do some benchmarking. So, I started using 'Pcre.bas' and 'RegEx.bas' but understood after some time that there is no option to make 'RegexMatch' function to do global matching. I was assuredly thinking that global matching would be definitely present in RegEx.bas since I thought VB6SourceProcessor would be definitely doing global matching. So, I spent a few hours in digging more - going through the VB6SourceProcessor code and VBPCRE2's 'cPcre2' class code. That led me to get to "my own understanding" that unless 'Execute2' kind of code (present in cPcre2 class) is present in 'RegEx.bas', global matching is not possible. When I find time, I shall try to include that kind of code in RegEx.bas and then do the benchmarking.
All said and done, if I am completely wrong in my above "my own understanding" and global pattern matching is indeed possible via RegEx.bas itself, my sincere apologies. In that case, if and when you find time, kindly let me know how to do global matching using the existing RegEx.bas code itself.
When time permits, I shall also study what Olaf has written in Post #29 (https://www.vbforums.com/showthread....=1#post5390811) and see whether it is possible to implement that solution as UDF too in SQLite SQL queries. If possible to implement, then I shall get to know more details from Olaf regarding Jscript9 - whether using Jscript9 is seamlessly supported in all versions of all Windows OSes (incl. Win11), etc.
By the way, I wanted to write the following in my earlier message itself but forgot. i.e. I simply loved the following which you have written in https://github.com/jpbro/VbPcre2. :) Had a hearty laugh.
--
Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems.
Some people when confronted with a problem, a regular expression library, and a need for access to said library in VB6 think "I know I'll write a wrapper for PCRE2 in VB6". Now they have three problems :)
--
:)
// 0% plans at this point unless you have a compelling reason? //
Well, I just thought that there may be some more advanced pattern matchings possible in the latest version of pcre2. Nothing else. From what you have written, it seems that that is not the case.
Thanks for your New Year wishes. :)
Kind Regards.
-
Re: VB6 Automated Source Code Processing Helper
Quote:
Originally Posted by
softv
I indeed wanted to do some benchmarking. So, I started using 'Pcre.bas' and 'RegEx.bas' but understood after some time that there is no option to make 'RegexMatch' function to do global matching. I was assuredly thinking that global matching would be definitely present in RegEx.bas since I thought VB6SourceProcessor would be definitely doing global matching. So, I spent a few hours in digging more - going through the VB6SourceProcessor code and VBPCRE2's 'cPcre2' class code. That led me to get to "my own understanding" that unless 'Execute2' kind of code (present in cPcre2 class) is present in 'RegEx.bas', global matching is not possible. When I find time, I shall try to include that kind of code in RegEx.bas and then do the benchmarking.
All said and done, if I am completely wrong in my above "my own understanding" and global pattern matching is indeed possible via RegEx.bas itself, my sincere apologies. In that case, if and when you find time, kindly let me know how to do global matching using the existing RegEx.bas code itself.
It's been a few years since I dug into pcre, but AFAIR there is no flag that replicates a "/g" or global search (if that functionality exists in a new version, that would be a compelling reason to update though!). I think you just have to loop against the previous end index+1 and build your own global resultset. That's probably one of the places where my old VbPcre2 code was inefficient and could be improved. You seem quite smart, so if you compare the full VbPcre2 matching code to the minimal module stuff, I think you will find your answer.
Anyway, give it a shot and you might spark another benchmarking thread...I think wqweto won the last one, but perhaps you will win the next ;)
[QUOTE=softv;5626996]When time permits, I shall also study what Olaf has written in Post #29 (https://www.vbforums.com/showthread....=1#post5390811) and see whether it is possible to implement that solution as UDF too in SQLite SQL queries. If possible to implement, then I shall get to know more details from Olaf regarding Jscript9 - whether using Jscript9 is seamlessly supported in all versions of all Windows OSes (incl. Win11), etc.
Quote:
Originally Posted by
softv
By the way, I wanted to write the following in my earlier message itself but forgot. i.e.
I simply loved the following which you have written in
https://github.com/jpbro/VbPcre2. :)
Had a hearty laugh.
--
Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems.
Some people when confronted with a problem, a regular expression library, and a need for access to said library in VB6 think "I know I'll write a wrapper for PCRE2 in VB6". Now they have three problems :)
--
:)
Hehe, and I didn't know it at the time, but it turns out the full quote should be:
Quote:
Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems.
Some people when confronted with a problem, a regular expression library, and a need for access to said library in VB6 think "I know I'll write a wrapper for PCRE2 in VB6". Now they have three problems.
Some people - upon "solving" the above problems, would never have thought that they would be supporting their "solutions" 5 years later
I'm joking of course! It's always nice when anything I've put out there gets a bit of use in the real world.
-
Re: VB6 Automated Source Code Processing Helper
There is a clear winner here, on the eve of the New Year. And, that is you, my dear JpBro! :) Well, based on "my own present needs", as far as "my own benchmarking tests" go, you have won indeed, so far. :)
Well, first of all, thanks for replying, so promptly again. I wonder how experts like you get time to reply so readily and promptly!
// if you compare the full VbPcre2 matching code to the minimal module stuff, I think you will find your answer. //
Yes, even before seeing your reply, I had read this answer (https://stackoverflow.com/a/70247959) 2 days back, at https://stackoverflow.com/questions/...ing-using-pcre - and that helped understand the need for the 'exec2' code in your vbpcre2, where extracting all matched strings is done based on ovector offsets.
Actually, based on my present need, which is just to search for string patterns in a database's words, I wrote a "pcrTest" routine (a minimal version of your RegExMatch routine) which would just tell whether match happened or not (i.e. just return True or False), given a string and a pattern.
Based on the above, for a non-English database (with around 40K unique words), my test results (all times in milliseconds), for pcr, jsr and vbr resp., were as follows:
==========
190-220, 150-170, 330-360 (when run in IDE)
110-130, 140-160, 110-130 (when run as exe) with Jscript9
190-220, 1500+, 330-360 (when run in IDE) with Jscript
110-130, 340+, 110-130 (when run as exe)
==========
pcr - using pcrTest, jsr - using ActiveScript, vbr - using VbRegEx
All timings were obtained using New_C.Timing. Was using GetTickCount earlier. 'New_c.Timing' was greatly convenient, after getting to know about it.
In my pattern, 2 negative lookaheads and 1 negative lookbehind were present in the case of pcr.
2 negative lookaheads alone were present in the case of jsr and vbr since they did not support lookbehinds.
I thought Jscript at least would support lookbehinds but it did not. May be that is also IE based Jscript which does not support lookbehind. I tried to see whether ActiveScript supports V8 for 'language'. It did not seem to support. Have to ask Olaf whether there is any way I can make ActiveScript use a flavour of Jscript which supports lookbehinds. I think if that flavor is V8, it would support all other possible features of RegEx too, as in PCRE2.
Coming to my pattern, since it has an extra lookbehind, that is more overhead (as per my understanding on what I read from net) but yet pcrTest excelled! In the jsr and vbr patterns, I handled the lookbehind using sqlite's substring. As far as I have read and understood from the net, it seems substring is somewhat faster than lookbehind (in the particular case pattern I have considered). If my understanding is correct, then, that adds further more applause to pcrTest!
Note-1: The pattern was returning around 40 records from the database (with around 40K rows), as result. As written in earlier messages, the pattern was passed to a UDF in a SqlQuery.
Note-2: Strangely, with substring used, Jscript did not perform well, compared to Jscript9, as one can see from the results.
Note-3: If and when I get time, I shall remember to run the tests in my Win11 system too and see the results.
Note-4: The timings include the process of loading the result rows in Krool's FlexGrid too. As of now, I am using Olaf's BindTo methodology (https://www.vbforums.com/showthread....=1#post5514140) for the same. I am yet to explore and use the recent 'FlexGrid' enhancements of Krool's.
For an English-only database (just a test database), my test results (all times in milliseconds), for pcr, jsr and vbr resp., were as follows:
==========
700 to 720, 680 to 700, 790-810 (when run in IDE) 'with Jscript9
570-590, 630-640, 550-570 (when run as exe) 'JSCRIPT9
570-590, 640-670, 550-570 (when run as exe) 'JSCRIPT
==========
One positive lookahead was present in my pattern for all of pcr, jsr and vbr.
The pattern was some random simple pattern "^(?=xyz).*" on a database of 50001 rows where all records are "xyzabc" except the last one which is "abcxyz". So, the number of records returned are 50K for the aforesaid pattern.
Actually, after doing my tests yesterday itself with just Jscript9 alone, I was thinking of sharing them alone today. But, today morning, after getting interest to see what you really meant by wqweto's tests, I went and saw that portion of the thread. So, I included Jscript also in my tests. By the way, the benchmarking test project provided in post #190 (by dreammanor) gives a very long time of 18+ seconds in my system (Win10, with 16GB RAM) for wqweto's method. I tried twice or thrice. I did not have time to explore to see why. May be a small tweak in wqweto's code, perhaps to meet some recent requirements, would do the trick. I dont know. Just as I wrote the aforesaid, I tested it again now and the results (in millisecs) are 4.6, 7.7, 5.6, 7.4, 19.2 for vbr, js9, js, pcr, wqw resp.
// that would be a compelling reason to update though! //
yes indeed. That would be a really compelling reason. In this context, I saw this - https://github.com/jpcre2/jpcre2 - it has a global matching option. Whoever has time (you, wqweto, ...) can look into how it is done in c++ wrapper and port it in vb6, if in case you find what is done in the c++ wrapper therein is better than all that is done so far with regard to vb pcre2 wrappers. I saw this too - https://www.pcre.org/current/doc/htm...est.html#SEC11 (Finding all matches in a string). So, may be one or other expert, if and when they find time, can study the code of pcre2Test too and see in what best optimized way it can be ported in vb6.
// You seem quite smart //
"Uhoh! I need to handle this". hahaha! :):):). Honestly, I am just a fledgling coder, compared to you all experts. I am just someone trying to make as best use as I can, of you all experts' monumental free sharing of your invaluable expertise, in my projects, and see what best benefits I can provide for free to the society, through my free apps. I wish you all experts were near me (with all the time in the world! :)) so that things like what I am asking here and there in the vbforums can be readily provided in a platter (as soon as asked) by experts like you so that the society gets benefited faster and faster. Well, I know that is not possible, as such. So, I chug along (so to say), plodding (so to say), with limited expertise (compared to you all experts), doing whatever best I can, in creating free apps, to help the society, with all of you experts' free help (which is being offered aplenty in our vbForums in so many different ways). And, feel immensely happy (for the opportunity to immensely Thank the Glory of the Love of the Lord Almighty for everything), when benefited users share their heartfelt and hearty happiness.
// I'm joking of course! //
:) Truly, its always a great feeling when experts like you add a bit of fun in your replies and coding. Particularly, when one is coding alone, with need for complex regexes too sometimes (as in your vb6SourceProcessor), one indeed needs a heavy dose of laughter, now and then. :). Thanks a lot for providing the same. I have started loving your way of writing like that (Uhoh!) before starting to handle special cases. :):):)
// It's always nice when anything I've put out there gets a bit of use in the real world. //
Coming to your vsp (vb6SourceProcessor), I have given an initial run to it and felt very glad that it accumulates lines for each and every form, bas, etc. So, I can work on it myself, when I find time, to build that kind of neat table which I mentioned about in one of my earlier posts. Having said that, if ever you find time to enhance your vsp, to meet my and many other users' needs, that would be great too. Recently, I tried RubberDuck but it could not parse my project's codes fully. So, I could not use any of its features. So, that way, your vsp would be of great help for me, in times to come, I believe. Already, the stats it has given me, for my present ongoing project is great and very useful. Thanks a TON. God Bless you. God Bless all.
In all Humbleness.
Kind Regards.
-
Re: VB6 Automated Source Code Processing Helper
Quote:
Originally Posted by
softv
For an English-only database (just a test database), my test results (all times in milliseconds), for pcr, jsr and vbr resp., were as follows:
==========
700 to 720, 680 to 700, 790-810 (when run in IDE) 'with Jscript9
570-590, 630-640, 550-570 (when run as exe) 'JSCRIPT9
570-590, 640-670, 550-570 (when run as exe) 'JSCRIPT
==========
One positive lookahead was present in my pattern for all of pcr, jsr and vbr.
The pattern was some random simple pattern "^(?=xyz).*" on a database of 50001 rows where all records are "xyzabc" except the last one which is "abcxyz". So, the number of records returned are 50K for the aforesaid pattern.
Not sure, how to interpret your test-results, because I get different timings (for 50,001 records)
FWIW...
Here's my JScript9-based UDF:
Code:
Option Explicit
Implements RC6.IFunction
Private WithEvents SC As cActiveScript, CO As Object
Private Sub Class_Initialize()
Set SC = New_c.ActiveScript("JScript9", False, False)
SC.AddCode "var oRegEx=null;" & vbCrLf & _
"function RegExInit(sPat){" & vbCrLf & _
" oRegEx = new RegExp(sPat, 'ig')" & vbCrLf & _
"}" & vbCrLf & _
"function RegExSearch(s){" & vbCrLf & _
" return (s.search(oRegEx) != -1)" & vbCrLf & _
"}"
Set CO = SC.CodeObject 'func-calls work fastest, when we use the CodeObject
End Sub
Private Property Get iFunction_DefinedNames() As String
iFunction_DefinedNames = "RegExp" 'tell SQLite, which functionname we are using
End Property
Private Sub iFunction_Callback(ByVal ZeroBasedNameIndex As Long, ByVal ParamCount As Long, UDF As cUDFMethods)
If ParamCount <> 2 Then UDF.SetResultError "RegExp needs two parameters!": Exit Sub
If UDF.GetType(2) = SQLite_NULL Then 'if the second param (the Field or Expression to search) is Null...
UDF.SetResultNull '...then return a Null here as well
Else 'normal case (no further sanity-checks)
Static stPat As String
If stPat <> UDF.GetText(1) Then 'make sure. to re-init the js-RegEx-Object only, when the pattern changes
stPat = UDF.GetText(1): CO.RegExInit UDF.GetText(1)
End If
UDF.SetResultInt32 CO.RegExSearch(UDF.GetText(2))
End If
End Sub
The above JScript9-based regexp-matcher then produces similar timing-results to the VBScript-based RegExp-COM-Object implementation.
(roughly 55msec to find the 50,000 matches) - though keep in mind, that the JScript9-regexp is more powerful regarding "support for complex patterns".
Here's InMemory-DB-based Test-Form-Code:
Code:
Option Explicit
Private Cnn As cConnection, Rs As cRecordset
Private Sub Form_Load()
Set Cnn = New_c.Connection(, DBCreateInMemory)
Cnn.AddUserDefinedFunction New cRegExpUDF2
Cnn.Execute "Create Table T(ID Integer Primary Key, Fld1 Text)"
Dim i As Long
For i = 1 To 50000
Cnn.ExecCmd "Insert Into T(Fld1) Values(?)", "xyzabc"
Next
Cnn.ExecCmd "Insert Into T(Fld1) Values(?)", "abcxyz" 'make it 50001 records
End Sub
Private Sub Form_Click()
New_c.Timing True
Set Rs = Cnn.GetRs("Select Count(*) From T Where Fld1 RegExp ?", "^(?=xyz).*")
Me.Caption = "FoundMatches: " & Rs(0).Value & New_c.Timing
End Sub
Sorry to jpbro, for posting all this regexp-stuff in this thread here...
Olaf
-
Re: VB6 Automated Source Code Processing Helper
Following is an extract from https://www.vbforums.com/showthread....=1#post5627191 (post #286)
--
Edit-1:
Just now, after posting the above, I checked out my referred post in JpBro's thread and I notice a new reply therein, from you!!!, Olaf. I will now go and read that fully.
Edit-2:
First of all, as ever, thanks a lot for your reply in JpBro's thread. I had not made that init/reinit optimisation (as in your code) in 'pcrTest' either. So, I started to make them (so that my future benchmarking will be on even scales). And, "to whatever level I could" do^ the optimisation, with my limited knowledge, and "to whatever level I have tested so far", as per my initial findings, I see a dramatic increase in processing speed in JpBro's pcrTest, w.r.t the non-English database. Once I have done a thorough testing for both non-English and English databases, I will share my results in JpBro's thread. At that time, I would request you to kindly help me know (if possible and if and when you find time) the ideal manner in which the pcrTest code shall be, so that it will carry the highest optimisation. In case my initial findings prove later to be not correct, I shall inform that also here. Thank you soooooo much once again for taking time, even amidst your hectic schedules, to give such clear-cut code snippets. Remaining in all humbleness, as always.
(^) I dont know whether I have handled the memory free-ings correctly. That's one more reason for which I have requested your kind help above. I wanted to post pcrTest code in my last reply in JpBro's thread itself but forgot.
--
With reference to the above, dear Olaf, I have tested more and so far I have not found any changes in my initial findings. JpBro's 'pcrTest' continues to remain much more fast than it was earlier, w.r.t the non-English database (even if I use substring instead of lookbehind). W.r.t the English database, I found 'pcrTest' to have become slightly faster. However, I would definitely like to test for some more time tomorrow. So, I shall post my revised test-results and also the 'pcrTest' code, some time tomorrow. Kindly please bear with me until then.
Thanks once again to both JpBro and you for all the help you both have rendered so far.
In all humbleness.
Kind regards.
-
2 Attachment(s)
Re: VB6 Automated Source Code Processing Helper
I've put together a project with slightly modified version of Olaf's test code (changed so that only 25,000 of the 50,000 rows will match just to simulate a more realistic workload). I've also added in my modPcre2.bas and modRegex.bas files, with a slight modification to cache the compiled regex handles and only cleanup/rebuild when the pattern changes (or you pass an empty pattern to force a cleanup) - thanks to Olaf for inspiring that change with his Jscript9 example.
Here it is:
Attachment 189734
After compiling with all the usual optimizations selected, PCRE2 gets ~2x the performance of JScript9 for me. Assuming I've haven't made a mistake in my test code, it would be interesting to see what results you guys are getting:
Attachment 189733
Quote:
Originally Posted by
Schmidt
Sorry to jpbro, for posting all this regexp-stuff in this thread here...
Olaf
No problem at all :) Though maybe it would have been better to have this conversation in the VBPCRE2 thread.
-
Re: VB6 Automated Source Code Processing Helper
Quote:
Originally Posted by
jpbro
After compiling with all the usual optimizations selected, PCRE2 gets ~2x the performance of JScript9 for me.
Assuming I've haven't made a mistake in my test code, it would be interesting to see what results you guys are getting:
Using your demo I get the same about factor 2 relation (in IDE/PCode ~1.5):
JScript9: 57msec
PCRE2: 24msec
Have first wondered, why there was no logic for "Pattern-Precompile/Caching" in your UDF-Class,
but then found it implemented directly in your *.bas-modules match-function...
So, there's not much more left over to optimize for the both cases I guess...
(unless the pcre-folks have worked wonders, and sped it up in recent years by another "factor x").
A regex-engine which is "making waves" in recent benchmarks, is the rust-crate "rure" -
Though have only found 64bit win-binaries in a longer google-session ...
(so far tried to avoid installing rust, compiling it myself)
Olaf
-
Re: VB6 Automated Source Code Processing Helper
Quote:
Originally Posted by
Schmidt
Using your demo I get the same about factor 2 relation (in IDE/PCode ~1.5):
JScript9: 57msec
PCRE2: 24msec
Thanks for confirming the results.
Quote:
Originally Posted by
Schmidt
Have first wondered, why there was no logic for "Pattern-Precompile/Caching" in your UDF-Class,
but then found it implemented directly in your *.bas-modules match-function...
Yeah, I figured it was a good feature to have in the base module, so I put it in there.
Quote:
Originally Posted by
Schmidt
So, there's not much more left over to optimize for the both cases I guess...
(unless the pcre-folks have worked wonders, and sped it up in recent years by another "factor x").
I took a dive through the PCRE2 release notes, and didn't see much related to performance, so I suspect you are right.
Quote:
Originally Posted by
Schmidt
A regex-engine which is "making waves" in recent benchmarks, is the rust-crate "rure" -
Though have only found 64bit win-binaries in a longer google-session ...
(so far tried to avoid installing rust, compiling it myself)
Thanks for the info, I'd not heard of "rure" before. I looked at the source and it doesn't seem too big (<700 lines?) unless I've missed something. I wonder if it would be possible to re-implement it in VB6 or if it makes use of any Rust language features that would make that impossible (I don't know enough about Rust to say)... I might be crazy enough to give it a shot if I can understand the Rust code well enough :afrog:
-
Re: VB6 Automated Source Code Processing Helper
Oh nevermind - if the regex-automata stuff is part of the whole shebang, then it's a heck of a lot of code.
-
Re: VB6 Automated Source Code Processing Helper
Quote:
Originally Posted by
jpbro
I looked at the source and it doesn't seem too big (<700 lines?) unless I've missed something. I wonder if it would be possible to re-implement it in VB6 or if it makes use of any Rust language features that would make that impossible (I don't know enough about Rust to say)...
LMAO I was looking at the rure.rs in the C API subfolder! I knew I was being optimistic :P I guess we could hope for a VB6 wrapper around this size, but we'd need a 32-bit DLL. Wish I had heard about this before the Christmas break, it might have been fun to try to build it and see how it performs.
-
Re: VB6 Automated Source Code Processing Helper
Quote:
Originally Posted by
softv
There is a clear winner here, on the eve of the New Year. And, that is you, my dear JpBro! :) Well, based on "my own present needs", as far as "my own benchmarking tests" go, you have won indeed, so far. :)
Woohoo, I won something! ;) JK, glad that something I worked on combined with your own study and work, produced a viable solution to your problem. You might want to replace modRegex.bas with the latest version in post #54 to see if it performs even better for you.
Quote:
Originally Posted by
softv
Well, first of all, thanks for replying, so promptly again. I wonder how experts like you get time to reply so readily and promptly!
Happy to help/reply as quickly as I can. As you can see be the previous couple "stupid mistake" posts, I'm not sure that I'm an expert yet - especially compared to some of the gurus that frequent this forum. That said, I can't speak for them, but I personally take great enjoyment in developing new knowledge while helping people here,
especially when they are inquisitive and show a propensity to internalize advice and go off and solve their specific problems themselves based on that advice like you have been doing :) Beyond the new stuff I learn personally, your investment and exploration of the issue shows growth, and that makes my time invested feel doubly worth it.
Quote:
Originally Posted by
softv
Actually, based on my present need, which is just to search for string patterns in a database's words, I wrote a "pcrTest" routine (a minimal version of your RegExMatch routine) which would just tell whether match happened or not (i.e. just return True or False), given a string and a pattern.
Based on the above, for a non-English database (with around 40K unique words), my test results (all times in milliseconds), for pcr, jsr and vbr resp., were as follows:
==========
190-220, 150-170, 330-360 (when run in IDE)
110-130, 140-160, 110-130 (when run as exe) with Jscript9
190-220, 1500+, 330-360 (when run in IDE) with Jscript
110-130, 340+, 110-130 (when run as exe)
==========
pcr - using pcrTest, jsr - using ActiveScript, vbr - using VbRegEx
All timings were obtained using New_C.Timing. Was using GetTickCount earlier. 'New_c.Timing' was greatly convenient, after getting to know about it.
There are some funky timings there, but I'd be interested to see your timing comparisons with my modified version of Olaf's test project in [URL="https://www.vbforums.com/showthread.php?869555-VB6-Automated-Source-Code-Processing-Helper&p=5627256&viewfull=1#post5627256"]post 54
I'm reading through the rest of your notes, but I have to take a break so I might not respond until tomorrow.
Happy New Year BTW :)
-
Re: VB6 Automated Source Code Processing Helper
Dear JpBro,
Happy Happy Happy New Year. :)
I feel joyous that my findings gave you happiness. Thanks for all your kind words. You should have been near me to see how happy I was, reading them. Ultimately, that was yet another opportunity for me to thank the Lord Almighty. It also made me feel that, finally finally finally, "perhaps" I can place one humble request of mine to a vbForum expert. About the same later.
Now, going through the recent replies from you and Olaf I am not sure whether you read my yesterday's post #53 of mine - https://www.vbforums.com/showthread....=1#post5627239 - if not read yet, kindly please read. Therein I have written that based on Olaf's wonderful tips, it became necessary for me to write my own optimized code for 'pcrTest' and that led to 'pcrTest' to run dramatically faster. I have also written that however I did not know whether all the optimization I did was done in the correct manner, since my coding proficiency is limited (compared to all you experts)
Well, in the context of all that I have written in my post #53, I share the timings of my test-results below, first, and then the 'pcrTest' code. All timings given below are average timings (on running the same tests quite a few times and observing the timings). The timings are in milliseconds.
For non-English Database (for pcr, jsr and vbr resp.)
--
63, 116, 69 (when I use substring in pcrTest pattern too so that the testing of "pcr,jsr,vbr" is on even scales)
55, 116, 69 (wow! this is when I use lookbehind instead of substring in pcrTest pattern)
--
If 63ms was great, 55ms is further more great. Really fantastic. Outstanding.Because, it all now looks instantaneous here for me to see Krool's flxGrid display the required records (based on Olaf's magical 'BindTo' method) in just 55 milliseconds. Instantaneousness is always the case whenever matching records are lesser for a particular 'search string, pattern string' pair, which is a great feel visually. Thanks, JpBro. :)
For English Database (for pcr, jsr and vbr resp.)
--
520, 600, 520
--
For the cnt(*) query of Olaf, just comparing pcr and jsr alone, resp.
--
52, 115
--
The code of 'pcrTest'
pcrTest's code is at the bottom of this message, to which I have not made any changes since my post #53. I thought I will make them (based on your own revised optimised code), after hearing your/Olaf's comments on my own optimized code of 'pcrTest'.
Actually, I started doing the optimization going by the 'Static' way only. But, later realised, that it will necessitate me to have a statement like "If p_RegexToMatch = vbNullString" (as in your code). So, I decided to just move the handles' declarations to the top of the module and have a separate cleanup routine (pcrCleanup). I decided to call this cleanup routine immediately after every call that is made to pcrTest in my project. In other words, this cleanup routine would be called only after the SqlQuery completes and fetches all the records to the flxGrid. And, I shall remember to call this cleanup routine every time pcrTest is called. I read in the internet that if IDE or app stops abruptly (for one or other reason), then unfreed memory handles will remain unfreed. So, I decided to call cleanup immediately after a call to pcrTest.
So, my question (to both of you, dear JpBro and dear Olaf) is whether the above-explained approach of mine is also correct in every way? If it is correct, then, shall I go ahead with it? Because, that would obviate the need to have a line like "If p_RegexToMatch = vbNullString" (which may consume a few milliseconds, I felt, esp. for large number of records).
But, on the other hand, if my approach is not correct and handles should never be declared at the module level (for some reason or other), and 'Static' is the right way to go about it, let me know the same too. Also, educate me on why handles should not be declared at the module level and cleaned up with a separate call, as done by me.
I noticed one thing. You are cleaning up the handles, JpBro, whenever the pattern changes. I have not done it yet in my pcrTest. Either I missed it (OR) I had a different idea about these handles at the time of coding.
So, if my approach is correct:
--
1. The one change I would have to do is to add the 'cleanup' call whenever pattern changes. Right? Kindly confirm. Also, I have passed "StrPtr(p_TextToSearch)" directly to the function call. Is that okay? Or, should I necessarily store it in l_StrPtr first, as you have done? If so, can those 2 lines involving l_StrPtr be optimized in any way?
2. As such, if and when you and/or olaf find time, you may kindly please correct all the mistakes in my code and give me your own version of 'pcrTest' with the highest/ideal optimization.
--
By the way, olaf, in your code, for my testing needs, I changed the line inside the 'RegExSearch' function to "return (oRegEx.test(s))"
// Woohoo, I won something! ;) JK, glad that something I worked on combined with your own study and work, produced a viable solution to your problem. You might want to replace modRegex.bas with the latest version in post #54 to see if it performs even better for you. ... .. . I personally take great enjoyment in developing new knowledge while helping people here, especially when they are inquisitive and show a propensity to internalize advice and go off and solve their specific problems themselves based on that advice like you have been doing :). Beyond the new stuff I learn personally, your investment and exploration of the issue shows growth, and that makes my time invested feel doubly worth it. //
I am happy that you appreciate all that I am trying to do as a coder - to study and check out certain things on my own too, so that apart from you experts' invaluable times being saved (to whatever extent), I learn more too, both from experts like you and from the internet also, and more importantly pass on the benefits of those learnings to the society through my free apps. In this particular case of RegEx-ing, I am happy it helped me find you as a winner too, JpBro. :). All this benchmarking actually started because there was no lookbehind in vbr! Otherwise, obviously, even few days back, I had no ideas on doing this kind of benchmarking at all. And, how the lack of lookbehind in vbr has helped in so many ways, ultimately helped the society! And, giving me one more opportunity to keep thanking the Lord Almighty.
Seeing the way you write with such personal touch and joy, so encouragingly, so kindly, so humbly, sprinkled with humour too :) at places, - all of which goes along with my wavelength, I thought "perhaps" I can place the request (mentioned by me at the start of this message) to you. In that regard, is it okay for me to send a 'private message' to you via VbForums, introducing myself? (since that is one way I know, as of now, of sending personal messages to you, which I believe will pave the way for "greater and faster" benefits to the society in times to come). If not okay, absolutely no problems. Actually, I have thought now and then in the past as to whether I shall request any one of the vbForum experts as to whether it is all right for me to send personal messages to them. But, I resisted, because you are all working under hectic schedules, I know. So, it would be quite unfair, to even to think of such a request. So, so far, I have never placed such a request to anyone, expert or otherwise. This is the first time ever I am placing a request of this sort.
Happy Happy Happy New Year, once again, to you, dear JpBro. :)
In all humbleness.
Kind Regards.
Code:
Option Explicit
Public Type Matches
FoundMatch As Boolean
MatchStart As Long
MatchLen As Long
Match As String
SubMatchCount As Long
SubMatches() As String
End Type
Private Declare Sub win32_CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)
Private l_CompiledContextHandle As Long
Private l_CompiledRegexHandle As Long
Private l_MatchDataHandle As Long
Private sPatPrev As String
'
Public Function RegexSplit(ByVal p_TextToSplit As String, Optional ByVal p_RegexToMatch As String) As String()
Dim la_Split() As String
Dim l_NextIndex As Long
Do
With RegExMatch(p_TextToSplit, p_RegexToMatch)
If .FoundMatch Then
' Found a match
' Make sure we have enough space for text in our result arrya
If l_NextIndex = 0 Then
ReDim la_Split(99)
ElseIf l_NextIndex > UBound(la_Split) Then
ReDim Preserve la_Split(l_NextIndex * 2)
End If
la_Split(l_NextIndex) = Left$(p_TextToSplit, .MatchStart - 1)
p_TextToSplit = Mid$(p_TextToSplit, .MatchStart + .MatchLen)
l_NextIndex = l_NextIndex + 1
Else
' No match found, exit loop
Exit Do
End If
End With
Loop
If l_NextIndex = 0 Then
ReDim la_Split(0)
la_Split(0) = p_TextToSplit
Else
If Len(p_TextToSplit) Then
If UBound(la_Split) < l_NextIndex Then ReDim Preserve la_Split(l_NextIndex)
la_Split(l_NextIndex) = p_TextToSplit
l_NextIndex = l_NextIndex + 1
End If
ReDim Preserve la_Split(l_NextIndex - 1)
End If
RegexSplit = la_Split
End Function
Public Function RegExMatch(ByVal p_TextToSearch As String, Optional ByVal p_RegexToMatch As String, Optional ByVal p_CaseSensitive As Boolean = False) As Matches
' Returns a Match UDT
' If .Matched = False then no matches were found
' If .Matched = True then:
' A match was found (with possible submatches depending on the regex).
' The full matched text will be stored in .Match as a string
' If there are sub-matches, then SubMatch count will be > 0
' You can retrieve sub-matches from the .SubMatches member using one-based indexing
' so .SubMatches(1) will return sub-match #1, .SubMatches(2) will return sub-match #2, etc...
' If .SubMatchCount = 0 then .SubMatches will not be dimensioned, so do not try to access it.
Dim l_CompiledContextHandle As Long
Dim l_CompiledRegexHandle As Long
Dim l_MatchDataHandle As Long
Dim l_MatchContextHandle As Long
Dim l_ErrorNumber As Long
Dim l_ErrorDesc As String
Dim l_MatchCount As Long
Dim l_OvectorPtr As Long
Dim la_Ovector() As Long
Dim l_StrPtr As Long
Dim l_ErrorCode As Long
Dim l_ErrorPosition As Long
Dim l_MatchStart As Long
Dim l_MatchLen As Long
Dim l_Flags As Long
Dim ii As Long ' Loop counter
'On Error GoTo ErrorHandler
l_CompiledContextHandle = pcre2_compile_context_create(0)
If l_CompiledContextHandle = 0 Then Err.Raise "Could not compile PCRE context! Last DLL Error: " & Err.LastDllError
If Not p_CaseSensitive Then
l_Flags = PCRE_CO_CASELESS Or PCRE_CO_MULTILINE
End If
l_CompiledRegexHandle = pcre2_compile(StrPtr(p_RegexToMatch), Len(p_RegexToMatch), l_Flags, l_ErrorCode, l_ErrorPosition, l_CompiledContextHandle)
If l_CompiledRegexHandle = 0 Then Err.Raise vbObjectError, , "Could not compile regex! Regex: " & p_RegexToMatch & vbNewLine & "Errorcode: " & l_ErrorCode & ", Error Position: " & l_ErrorPosition
l_MatchDataHandle = pcre2_match_data_create_from_pattern(l_CompiledRegexHandle, 0)
If l_MatchDataHandle = 0 Then Err.Raise vbObjectError, , "Could not allocate match data! Last DLL Error: " & Err.LastDllError
l_StrPtr = StrPtr(p_TextToSearch)
If l_StrPtr = 0 Then l_StrPtr = StrPtr("")
l_MatchCount = pcre2_match(l_CompiledRegexHandle, l_StrPtr, Len(p_TextToSearch), 0, 0, l_MatchDataHandle, l_MatchContextHandle)
Select Case l_MatchCount
Case PCRE2_ERROR_NOMATCH
' No matches, that's normal :)
Case Is > 0
' Number of matches, store information about matches
l_OvectorPtr = pcre2_get_ovector_pointer(l_MatchDataHandle)
If l_OvectorPtr = 0 Then
' Shouldn't happen!
Err.Raise vbObjectError, , "Ovector pointer could not be retrieved!"
End If
win32_CopyMemory l_MatchStart, ByVal l_OvectorPtr, 4
win32_CopyMemory l_MatchLen, ByVal (l_OvectorPtr + 4), 4
l_MatchLen = l_MatchLen - l_MatchStart
ReDim la_Ovector(2 * l_MatchCount - 1)
win32_CopyMemory la_Ovector(0), ByVal l_OvectorPtr, 2 * l_MatchCount * 4
With RegExMatch
.FoundMatch = l_MatchCount
.MatchStart = la_Ovector(0) + 1
.MatchLen = la_Ovector(1) - la_Ovector(0)
.Match = Mid$(p_TextToSearch, .MatchStart, .MatchLen)
.SubMatchCount = l_MatchCount - 1
If l_MatchCount > 1 Then
ReDim .SubMatches(1 To l_MatchCount - 1)
For ii = 1 To l_MatchCount - 1
l_MatchStart = la_Ovector(ii * 2) + 1
l_MatchLen = la_Ovector(ii * 2 + 1) - l_MatchStart + 1
If l_MatchLen > 0 Then
.SubMatches(ii) = Mid$(p_TextToSearch, l_MatchStart, l_MatchLen)
End If
Next ii
End If
End With
Case Else
' Uhoh! We need to handle these
Err.Raise vbObjectError - l_MatchCount, , "PCRE Match Error: " & l_MatchCount
End Select
Cleanup:
'On Error Resume Next
' Free match data if necessary
If l_MatchContextHandle <> 0 Then pcre2_match_context_free l_MatchContextHandle: l_MatchContextHandle = 0
If l_MatchDataHandle <> 0 Then pcre2_match_data_free l_MatchDataHandle: l_MatchDataHandle = 0
If l_CompiledRegexHandle <> 0 Then pcre2_code_free l_CompiledRegexHandle: l_CompiledRegexHandle = 0
'Free compile context before exiting
If l_CompiledContextHandle <> 0 Then pcre2_compile_context_free l_CompiledContextHandle: l_CompiledContextHandle = 0
If l_ErrorNumber <> 0 Then
If IsPcre2ErrorCode(l_ErrorNumber) Then
l_ErrorDesc = l_ErrorDesc & vbNewLine & "PCRE2 Error Message: " & GetPcre2ErrorMessage(l_ErrorNumber)
Else
If IsPcre2ErrorCode(vbObjectError - l_ErrorNumber) Then
l_ErrorDesc = l_ErrorDesc & vbNewLine & "PCRE2 Error Message: " & GetPcre2ErrorMessage(vbObjectError - l_ErrorNumber)
End If
End If
On Error GoTo 0
Err.Raise l_ErrorNumber, , l_ErrorDesc
End If
Exit Function
ErrorHandler:
l_ErrorNumber = Err.Number
l_ErrorDesc = Err.Description
Debug.Assert False
Resume Cleanup
End Function
Private Function IsPcre2ErrorCode(ByVal p_ErrorCode As Long) As Boolean
IsPcre2ErrorCode = (p_ErrorCode <= [_PCRE_RC_ERROR_FIRST] And p_ErrorCode >= [_PCRE_RC_ERROR_LAST])
End Function
Private Function GetPcre2ErrorMessage(ByVal p_ErrorCode As Long) As String
Dim l_BufferLength As Long
Dim l_Buffer As String
Dim l_MessageLength As Long
l_BufferLength = 256
Do
l_Buffer = Space$(l_BufferLength)
l_MessageLength = pcre2_get_error_message(p_ErrorCode, StrPtr(l_Buffer), l_BufferLength)
If l_MessageLength < 0 Then
Select Case l_MessageLength
Case PCRE_RC_ERROR_NOMEMORY
' Buffer too small
l_BufferLength = l_BufferLength * 2
Case PCRE_RC_ERROR_BADDATA
' Bad error code
Exit Do
Case Else
Debug.Assert False
Exit Do
End Select
End If
Loop While l_MessageLength < 0
If l_MessageLength < 0 Then
GetPcre2ErrorMessage = "Unknown error #" & p_ErrorCode & ", PCRE2 error message result #" & l_MessageLength
Else
GetPcre2ErrorMessage = Left$(l_Buffer, l_MessageLength)
End If
End Function
'
Public Function PcrTest(ByVal p_TextToSearch As String, Optional ByVal p_RegexToMatch As String, Optional ByVal p_CaseSensitive As Boolean = False) As Boolean
' Returns a Match UDT
' If .Matched = False then no matches were found
' If .Matched = True then:
' A match was found (with possible submatches depending on the regex).
' The full matched text will be stored in .Match as a string
' If there are sub-matches, then SubMatch count will be > 0
' You can retrieve sub-matches from the .SubMatches member using one-based indexing
' so .SubMatches(1) will return sub-match #1, .SubMatches(2) will return sub-match #2, etc...
' If .SubMatchCount = 0 then .SubMatches will not be dimensioned, so do not try to access it.
'Static l_CompiledContextHandle As Long
'Static l_CompiledRegexHandle As Long
'Static l_MatchDataHandle As Long
Dim l_MatchContextHandle As Long
Dim l_ErrorNumber As Long
Dim l_ErrorDesc As String
Dim l_MatchCount As Long
'Dim l_OvectorPtr As Long
'Dim la_Ovector() As Long
''''''''''''''''''''''''''''''''''''''''''''Dim l_StrPtr As Long
Dim l_ErrorCode As Long
Dim l_ErrorPosition As Long
'Dim l_MatchStart As Long
'Dim l_MatchLen As Long
Dim l_Flags As Long
'''''Dim ii As Long ' Loop counter
On Error GoTo ErrorHandler
'If Trim$(p_TextToSearch) = vbNullString Then
'Exit Sub
'End If
If sPatPrev <> p_RegexToMatch Then
'If Not p_CaseSensitive Then
'End If
l_Flags = PCRE_CO_CASELESS
sPatPrev = p_RegexToMatch
l_CompiledContextHandle = pcre2_compile_context_create(0)
If l_CompiledContextHandle = 0 Then Err.Raise "Could not compile PCRE context! Last DLL Error: " & Err.LastDllError
l_CompiledRegexHandle = pcre2_compile(StrPtr(p_RegexToMatch), Len(p_RegexToMatch), l_Flags, l_ErrorCode, l_ErrorPosition, l_CompiledContextHandle)
If l_CompiledRegexHandle = 0 Then Err.Raise vbObjectError, , "Could not compile regex! Regex: " & p_RegexToMatch & vbNewLine & "Errorcode: " & l_ErrorCode & ", Error Position: " & l_ErrorPosition
l_MatchDataHandle = pcre2_match_data_create_from_pattern(l_CompiledRegexHandle, 0)
If l_MatchDataHandle = 0 Then Err.Raise vbObjectError, , "Could not allocate match data! Last DLL Error: " & Err.LastDllError
End If
'l_StrPtr = StrPtr(p_TextToSearch)
'''''If l_StrPtr = 0 Then l_StrPtr = StrPtr("")
'''''l_MatchCount = pcre2_match(l_CompiledRegexHandle, l_StrPtr, Len(p_TextToSearch), 0, 0, l_MatchDataHandle, l_MatchContextHandle)
l_MatchCount = pcre2_match(l_CompiledRegexHandle, StrPtr(p_TextToSearch), Len(p_TextToSearch), 0, 0, l_MatchDataHandle, l_MatchContextHandle)
Select Case l_MatchCount
Case PCRE2_ERROR_NOMATCH
' No matches, that's normal :)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''pcrTest = False
Case Is > 0
' Number of matches, store information about matches
PcrTest = True
Case Else
' Uhoh! We need to handle these
Err.Raise vbObjectError - l_MatchCount, , "PCRE Match Error: " & l_MatchCount
End Select
'''''''''''''''''''''Exit Function
Cleanup:
'On Error Resume Next
' Free match data if necessary
If l_MatchContextHandle <> 0 Then pcre2_match_context_free l_MatchContextHandle: l_MatchContextHandle = 0
'''''If l_MatchDataHandle <> 0 Then pcre2_match_data_free l_MatchDataHandle: l_MatchDataHandle = 0
'''''If l_CompiledRegexHandle <> 0 Then pcre2_code_free l_CompiledRegexHandle: l_CompiledRegexHandle = 0
'Free compile context before exiting
'''''If l_CompiledContextHandle <> 0 Then pcre2_compile_context_free l_CompiledContextHandle: l_CompiledContextHandle = 0
If l_ErrorNumber <> 0 Then
If IsPcre2ErrorCode(l_ErrorNumber) Then
l_ErrorDesc = l_ErrorDesc & vbNewLine & "PCRE2 Error Message: " & GetPcre2ErrorMessage(l_ErrorNumber)
Else
If IsPcre2ErrorCode(vbObjectError - l_ErrorNumber) Then
l_ErrorDesc = l_ErrorDesc & vbNewLine & "PCRE2 Error Message: " & GetPcre2ErrorMessage(vbObjectError - l_ErrorNumber)
End If
End If
On Error GoTo 0
Err.Raise l_ErrorNumber, , l_ErrorDesc
End If
Exit Function
ErrorHandler:
l_ErrorNumber = Err.Number
l_ErrorDesc = Err.Description
Debug.Assert False
Resume Cleanup
End Function
'
Public Sub pcrCleanup()
sPatPrev = ""
If l_MatchDataHandle <> 0 Then pcre2_match_data_free l_MatchDataHandle: l_MatchDataHandle = 0
If l_CompiledRegexHandle <> 0 Then pcre2_code_free l_CompiledRegexHandle: l_CompiledRegexHandle = 0
If l_CompiledContextHandle <> 0 Then pcre2_compile_context_free l_CompiledContextHandle: l_CompiledContextHandle = 0
End Sub
'