Regular expressions in AL
Views (2146)
Purposes
1 Introduce, but not explain, regular expressions (Regex hereinafter). But even if I am not going to explain here Regex, I think you don“t have to know a lot about it to follow the AL example.
2 Share regex resources. A special mention to regex101 site https://regex101.com/. With this site you can learn a lot about regex by yourselves, in a comfortable and non-theorical way, with a library of already made regular expressions. For a quick start in regex world is the best choice.
3 Use Regex AL objects in an example.
Regular expressions
Regular expressions are a set of characters that define a search pattern in strings. Regex are usually used to find and search operations or input validations (VAT numbers, post codes, phone numbers, email) checking if the input matches with the defined pattern.
Most user languages as JavaScript, .NET have objects to manage regex, usually with these methods: execute, test, match, and replace. Regex is very useful, but a bit complex and sometimes programmersā trend to shoehorn it in every string problem, and they donĀ“t fit always for everything, as said Marijn Haverbeke in his book āEloquent JavaScriptā.
The base concepts of Regex are pattern, grouping, quantification and wildcard. I am not going to explain an over-explained subject as Regex , so here we are some resources for a quick start in regex:
Wikipedia (basics concepts):
Brief, good and free book:
Great and free book, chapter 9 āRegular expressionā:
And a page to test your own expressions (with a complete breakdown or the results and assistant) and a lot of common use case examples:
AL Regex example
Let`s do an AL example, and breakdown it to know more about Regex management in AL. A good āHello worldā for Regex in AL development could be this well-known string problem: variable naming in AL. I mean, replacing this line of code:
BadName: Record "Sales Header";
with this line:
SalesHeader: Record "Sales Header";
The steps to solve this problem with Regex are:
1 Create a new Codeunit and define the search pattern for the AL object variable declaration
DeclarationPatternlbl: Label '(.*):\s*(Record|Page|Report|Codeunit|XMLPort|Query)(.*);';
Let“s take a break of AL code and go to a previously recommended page https://regex101.com/ to get a complete analysis of this pattern. This site allow us to save a pattern, and this pattern is saved as https://regex101.com/r/klMsgX/1/. When we open the link, we can see a complete breakdown of the pattern with an example:
In the āExplanationā section we can find this pattern breakdown:
Pattern= (.*):\s*(Record|Page|Report|Codeunit|XMLPort|Query)(.*); (In patterns, groups are between parentheses).
1 We have a group (.*): that matches any character until ā:ā character.
2 \s* matches any whitespace character.
3 Another group to return object type: (Record|Page|Report|Codeunit|XMLPort|Query). The pipeline means alternative in regex patterns.
4 And the last group, all the characters until the ā;ā character: (.*);
In the āMatch informationā section we have the match example results, a full match with three groups:
1 Full match: BadName: Record "Sales Header";
2 Group 1: BadName.
3 Group 2: Record.
4 Group 3: "Sales Header".
Now let“s go back to AL Visual Studio Code to continue the example.
2 Create the functions and declare the variable objects we will need:
procedure RenameALObjectVariable(OriginalStatement: Text) FinalStatement: text;
var
DotNet_Regex: Codeunit DotNet_Regex;
DotNet_RegexOptions: Codeunit DotNet_RegexOptions;
DotNet_Match: Codeunit DotNet_Match;
DotNet_GroupVarType: Codeunit DotNet_Group;
DotNet_GroupVarOldVarName: Codeunit DotNet_Group;
DotNet_GroupVarSubType: Codeunit DotNet_Group;
DotNet_GroupCollection: Codeunit DotNet_GroupCollection;
NewVarName: Text[100];
NameAlreadyInOldName: Boolean;
All these Al objects are encapsulations of existing DotNet objects that manage regular expressions.
3 Find the AL variable declaration pattern in the string
If you don“t find it, you skip the process with exit statement:
procedure RenameALObjectVariable(OriginalStatement: Text) FinalStatement: text;
var
ā¦ā¦..
begin
FinalStatement := OriginalStatement;
DotNet_RegexOptions.IgnoreCase();
DotNet_Regex.Match(OriginalStatement, DeclarationPatternlbl, DotNet_RegexOptions, DotNet_Match);
if not DotNet_Match.Success() then
exit;
4 Catch the groups of the match
We have three groups: old variable name, variable type and group subtype.
DotNet_Match.Groups(DotNet_GroupCollection);
DotNet_GroupCollection.Item(1, DotNet_GroupVarOldVarName);
DotNet_GroupCollection.Item(2, DotNet_GroupVarType);
DotNet_GroupCollection.Item(3, DotNet_GroupVarSubType);
5 Assemble new declaration statement to return it
This way Badname: Record āSales Headerā; becomes SalesHeader: Record āSales Headerā;
NewVarName := DelChr(DotNet_GroupVarSubType.Value(), '=', '" ');
FinalStatement := NewVarName + ': ' + DotNet_GroupVarType.Value() + ' ' + DotNet_GroupVarSubType.Value() + ';';
*This post is locked for comments