Stringier.Patterns
0.9.0
Features merged.
dotnet add package Stringier.Patterns --version 0.9.0
NuGet\Install-Package Stringier.Patterns -Version 0.9.0
<PackageReference Include="Stringier.Patterns" Version="0.9.0" />
paket add Stringier.Patterns --version 0.9.0
#r "nuget: Stringier.Patterns, 0.9.0"
// Install Stringier.Patterns as a Cake Addin
#addin nuget:?package=Stringier.Patterns&version=0.9.0
// Install Stringier.Patterns as a Cake Tool
#tool nuget:?package=Stringier.Patterns&version=0.9.0
Stringier.Patterns
Patterns, probably introduced with SNOBOL, and also seen with SPITBOL and UNICON, are considerably more powerful than Regular Expressions. So what do you do when you need to parse something more complicated than a Regex? Hacky Regex extensions aren't great, and still lack what some advanced alternatives can offer. Parser Combinators? Actually these are great. I'm not going to bash them at all. Pattern Matching and Parser Combinators share a huge amount of theory and implementation details. You could consider them alternative interpretations of the same concept. That being said, you'll notice a few small differences, but the advantages apply to both.
Including
using System.Text.Patterns;
Usage
In most situations, there's only three usage patterns you'll need to know.
Declaration
Pattern patternName = "Text to match";
// Comparison of a Literal
or
Pattern patternName = ("Text to match", StringComparison.CurrentCultureIgnoreCase);
// Comparisons of this Literal will use the StringComparison value
or
Pattern patternName = literalPattern1 & (literalPattern2 | literalPattern3);
// Comparison of an actual pattern
Matching
patternName.Consume("Candidate text");
//Assuming Consume captures "Candidate" this will return true and "Candidate"
Inline (Quick Match)
"Hello".Consume("Hello World!");
//Assuming "Hello" captures "Hello" (which it obviously will) this will return true and "Hello"
Concepts
Multiple return values
Pattern matching is largely based around the idea of goal-direction. The two most likely languages you're using this library from C# and VB.NET don't support goal-direction (if you're using F# then FParsec is going to match that programming style better anyways). Goal-directed semantics require both a success state and the result to be returned from every function call (or just the success state for a void
return).
But wait, C# can't return multiple values!
While true, this is remarkably pedantic. Whether you return an array, a struct, a class, or a tuple, you are returning multiple values as one conceptual value. All the parsing methods return Result
which contains both the success state (Boolean
) and the result of the operation (String
). Result
implicitly casts to both Boolean
and String
and can be used as such. This allows some conveniences without adding new methods.
So every return passes two values? Isn't that a lot of extra memory?
One, no not really, a single Boolean
isn't very large. Two, it doesn't actually pass a Boolean
at all. An empty string is recognized as a failure. Essentially Result
is a box of String
with special comparisons and implicit conversions. In other words, the behavior of Parse
and TryParse
combined into one method. And, getting technical, we're not actually passing around String
either. We're actually passing around Span<Char>
for performance reasons; actually passing around references to parts of the string, preventing copying in most situations.
Literal
Pattern patternName = "Literal Pattern";
This is an exact 1:1 match pattern, and is equivalent to
"pattern" == "candidate"`
Literal is meant mostly as a building block for patterns. Because pattern operators expect to use a Literal, which is not a string, the convenient syntax shown above only applies to Literal. Use inside a pattern operator might require a cast like
(Pattern)"Literal Pattern" & "Other Literal Pattern"
This is generally only required as the very first member
Alternator
Pattern patternName = pattern1 | pattern2;
Alternators accept either pattern, and are equivalent to the regex (pattern1|pattern2)
.
Combinator
Pattern patternName = pattern1 & pattern2;
Combinators require both patterns in sequence and are equivalent to the regex (pattern1)(pattern2)
with the unnecessary parenthesis added for readability.
Negator
Pattern patternName = !pattern;
Negators exclude the specified pattern, instead consuming anything of the same length. This is not a concept easy to express with regex, but given the pattern !"hi"
it would be similar to /[^h][^i]/
.
Optor
Pattern patternName = ~pattern;
Optors make the pattern completly optional, so success is always true, and are equivalent to the regex (pattern)?
.
Ranger
Pattern patternName = (From: startPattern, To: endPattern);
or
Pattern patternName = (From: startPattern, To: endPattern, Escape: escapePattern);
Rangers are totally foreign to regex, also some parsers are able to synthesize the behavior. The behavior is to simply try matching the From
at the current position, then continue to read everything until the To
is matched, which also consumes that. Optionally, an Escape
may be defined, which is attempted to be matched before To
and is used primarily for matching string literals which have language defined escape sequences for the delimiting character.
To provide a few examples of how this behavior is useful:
Pattern letStatement = (From: "let", To: ";"); //This will match an entire let statement in a language which has semicolon terminated statements
Pattern cString = (From: "\"", To: "\"", Escape: "\\\""); //This will match an entire C string literal, while including double-quote escapes
Repeater
Pattern patternName = pattern * 3; //repeats the pattern three times
Repeaters require the pattern to repeat the specified number of times, and can be thought of the multiplcation to patterns when combinators are addition. The above example would be equivalent to the regex (pattern){3}
.
Spanner
Pattern patternName = +pattern;
Spanners require the pattern to exist at least once, but will repeat until the pattern can no longer be matched, and are equivalent to the regex (pattern)+
.
Checker
Pattern patternName = (Pattern)((Char) =>
//Logic
);
Checkers aren't normally used, but seeing as they form the basis of a lot of predefined patterns because of their usability, they are also exposed publicly to enable certain complex patterns or greatly simplify complex patterns. Instead of doing checks through the pattern API, the check, held only against a single character, is done programmatically. The check is a lamba which takes a single character and returns a boolean. Anything can be done in this context. Overwhelmingly you don't want to use this.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
.NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Stringier (>= 1.13.0)
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 |
---|