ModularExpressions 1.0.2

dotnet add package ModularExpressions --version 1.0.2
                    
NuGet\Install-Package ModularExpressions -Version 1.0.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ModularExpressions" Version="1.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ModularExpressions" Version="1.0.2" />
                    
Directory.Packages.props
<PackageReference Include="ModularExpressions" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ModularExpressions --version 1.0.2
                    
#r "nuget: ModularExpressions, 1.0.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ModularExpressions@1.0.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ModularExpressions&version=1.0.2
                    
Install as a Cake Addin
#tool nuget:?package=ModularExpressions&version=1.0.2
                    
Install as a Cake Tool

ModularExpressions for C#

ModularExpressions logo

Project Description

ModularExpressions is a Rosyln-powered generator for regular expressions from .NET syntax. With this ModularExpressions package, C# developers can create methods that return regular-expression patterns from basic C# modules and compile-time constants. It therefore allows developers to employ regular expressions using readable, maintainable C# code without having any familiarity with the Regex language.

The ModularExpressions package uses a Roslyn source-generator, so these regular-expression patterns are generated at compile-time.

Install and Setup

To use Modular Expressions, include the ModularExpressions NuGet package in your C# project.

It is also recommended to add the following to the .csproj file of that project (see Usage for explanation):

<ItemGroup>
  <Using Include="ModularExpressions.Modex" Static="true" />
</ItemGroup>

Usage

To create a Modular Expression, first create an arrow-property with Modex as its return-type in the class where you want the regular-expression pattern generated, and have it return the Modular Expression you want. (See Modex Elements below for a list of available elements.)

For example, to generate a pattern that matches any text that looks like a decimal number with digit grouping:

private static Modex GroupedDecimalNumber => ZeroOrOneOf("-") + (Digit.TimesBetween(min: 1, max: 3) + ZeroOrMoreOf("," + Digit.Times(3)) + ZeroOrOneOf("." + ZeroOrMoreOf(Digit)) | "." + OneOrMoreOf(Digit));

Then, create a partial parameterless method declaration (i.e. without a body) that returns a string, and decorate it with the GenerateModex attribute, with the name of the Modex property as argument.

For the decimal-number example above, it would look like:

[GenerateModex(nameof(GroupedDecimalNumber))]
public static partial string GroupedDecimalNumberPattern();

private static Modex GroupedDecimalNumber => ZeroOrOneOf("-") + (Digit.TimesBetween(min: 1, max: 3) + ZeroOrMoreOf("," + Digit.Times(3)) + ZeroOrOneOf("." + ZeroOrMoreOf(Digit)) | "." + OneOrMoreOf(Digit));

Of course, the containing class would then have to be declared partial as well. (A compilation error would occur if not.)

And that's it. Now, every time GroupedDecimalNumberPattern() is called, it returns the correct regular-expression pattern matching any text that looks like a decimal number with digit grouping.

Note: the <Using Include="ModularExpressions.Modex" Static="true" /> element mentioned in the Install and Setup section above allows all those built-in Modex elements (Digit, ZeroOrOneOf and so on) to appear in the example above without any prefix. If that element is not added to the .csproj file as recommended, then every such built-in Modex element would have to be prefixed with its (Modex) class, making the example above look like:

[GenerateModex(nameof(GroupedDecimalNumber))]
public static partial string GroupedDecimalNumberPattern();

private static Modex GroupedDecimalNumber => Modex.ZeroOrOneOf("-") + (Modex.Digit.TimesBetween(min: 1, max: 3) + Modex.ZeroOrMoreOf("," + Modex.Digit.Times(3)) + Modex.ZeroOrOneOf("." + Modex.ZeroOrMoreOf(Modex.Digit)) | "." + Modex.OneOrMoreOf(Modex.Digit));

The pattern is not understood immediately, so the extra verbosity might deteriorate readability.

Modex XML-Documentation

A new <modex /> element is supported in the XML documentation of the string method that returns the regular-expression pattern. This allows the developer to see the returned pattern without having to run or debug the code.

If the decimal-number example above is changed to:

/// <summary>
/// Returns the pattern <b><modex /></b> that matches any text that looks like a decimal number with digit grouping.
/// </summary>
[GenerateModex(nameof(GroupedDecimalNumber))]
public static partial string GroupedDecimalNumberPattern();

private static Modex GroupedDecimalNumber => ZeroOrOneOf("-") + (Digit.TimesBetween(min: 1, max: 3) + ZeroOrMoreOf("," + Digit.Times(3)) + ZeroOrOneOf("." + ZeroOrMoreOf(Digit)) | "." + OneOrMoreOf(Digit));

then hovering the mouse over the GroupedDecimalNumberPattern() method (or starting to type-out its name) will show the following hint:

Returns the pattern <b>"-?(\\d{1,3}(,\\d{3})(\\.\\d)?|\\.\\d+)"</b> that matches any text that looks like a decimal number with digit grouping.

This makes it clear that the developer has managed to generate an element of the Regex language with nothing more than what C# provides and, indeed, without even having to know the first thing about the Regex language.

Modex References

One Modex-returning arrow-property can reference another. This can be done recursively (as long as there is no reference cycle) to reduce duplication and make the Modex-returning properties themselves more readable and manageable.

So the decimal-number example above can be rephrased more elegantly as:

/// <summary>
/// Returns the pattern <b><modex /></b> that matches any text that looks like a decimal number with digit grouping.
/// </summary>
[GenerateModex(nameof(GroupedDecimalNumber))]
public static partial string GroupedDecimalNumberPattern();

private static Modex GroupedDecimalNumber => OptionalMinus + (DecimalNumberWithAWholePart | DecimalNumberWithoutAWholePart);

private static Modex OptionalMinus => ZeroOrOneOf("-");

private static Modex DecimalNumberWithAWholePart => OneToThreeDigits + OptionalDigitGroups + OptionalFractionPart;

private static Modex OneToThreeDigits => Digit.TimesBetween(min: 1, max: 3);

private static Modex OptionalDigitGroups => ZeroOrMoreOf(DigitGroup);

private static Modex DigitGroup => "," + ThreeDigits;

private static Modex ThreeDigits => Digit.Times(3);

private static Modex OptionalFractionPart => ZeroOrOneOf(DecimalNumberWithoutAWholePart);

private static Modex DecimalNumberWithoutAWholePart => "." + OneOrMoreDigits;

private static Modex OneOrMoreDigits => OneOrMoreOf(Digit);

Modex Elements

The following list shows all elements available in the ModularExpressions package: |Element |Static |Example Modex |Resulting Pattern | |-----------------------------------------------|------------------------|-----------------------------------------------------------|------------------------------| |Tab |:heavy_check_mark: |Tab |"\\t" | |CarriageReturn |:heavy_check_mark: |CarriageReturn |"\\r" | |NewLine |:heavy_check_mark: |NewLine |"\\n" | | | | | | |AnyCharacterIn(characterClassElements) |:heavy_check_mark: |AnyCharacterIn('Z', '2', Tab) |"[Z2\\t]" | |AnyCharacterNotIn(characterClassElements)|:heavy_check_mark: |AnyCharacterNotIn('b', NewLine, '$') |"[^b\\n$]" | |CharacterRange(first, last) |:heavy_check_mark: |AnyCharacterIn('_', CharacterRange('A', 'M')) |"[_A-M]" | |NonNewLineCharacter |:heavy_check_mark: |NonNewLineCharacter |"." | |WordCharacter |:heavy_check_mark: |WordCharacter |"\\w" | |NonWordCharacter |:heavy_check_mark: |NonWordCharacter |"\\W" | |WhitespaceCharacter |:heavy_check_mark: |WhitespaceCharacter |"\\s" | |NonWhitespaceCharacter |:heavy_check_mark: |NonWhitespaceCharacter |"\\S" | |Digit |:heavy_check_mark: |Digit |"\\d" | |NonDigit |:heavy_check_mark: |NonDigit |"\\D" | | | | | | |Beginning |:heavy_check_mark: |Beginning |"^" | |End |:heavy_check_mark: |End |"$" | |WordBoundary |:heavy_check_mark: |WordBoundary |"\\b" | |WordNonBoundary |:heavy_check_mark: |WordNonBoundary |"\\B" | | | | | | |NamedGroup(name, subexpression) |:heavy_check_mark: |NamedGroup("beginWithDigit", Beginning + Digit)|"(?<beginWithDigit>^\\d)"| | | | | | |ZeroOrMoreOf(subexpression) |:heavy_check_mark: |ZeroOrMoreOf(WordCharacter) |"\\w*" | |OneOrMoreOf(subexpression) |:heavy_check_mark: |OneOrMoreOf(Digit | NonWordCharacter) |"(\\d|\\S)+" | |ZeroOrOneOf(subexpression) |:heavy_check_mark: |ZeroOrOneOf(WordBoundary) |"\\b?" | |Times(times) |:heavy_multiplication_x:|"aBc".Times(3) |"(aBc){3}" | |TimesAtLeast(times) |:heavy_multiplication_x:|NonDigit.TimesAtLeast(6) |"\\D"{6,} | |TimesBetween(min, max) |:heavy_multiplication_x:|Digit.TimesBetween(10, 20) |"\\d{10,20}" |

As can be seen in the NamedGroup example above, Modex elements can be concatenated by a + operator.

As can be seen in the OneOrMoreOf example above, a | operator can be used for alternating between Modex elements.

As can be seen in the Times example above, a string can serve as a Modex element by itself.

Contribute

This is a living, developing project, so it might not cover everything that comes to mind.

If you have a need for a specific regular-expression pattern not supported by this package, or if you find a problem, bug or mismatch with the official Regex language, please contact me using the Contact owners → link on the NuGet page and I will do my best to comply.

Enjoy!

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.2 124 10/24/2024
1.0.1 108 10/16/2024
1.0.0 116 10/12/2024