|
1 | 1 | #  |
2 | | - |
| 2 | + |
3 | 3 |
|
4 | | -Flint is a lightweight, zero-dependency C# library that provides fast text matching utilities using the [Aho-Corasick algorithm](https://en.wikipedia.org/wiki/Aho%E2%80%93Corasick_algorithm). |
| 4 | +STYME is a lightweight, zero-dependency C# library for parsing simple natural-language date/time expressions and applying them to a base date/time. |
5 | 5 |
|
6 | 6 | ## Usage Examples |
7 | 7 |
|
8 | 8 | ### Basic |
9 | 9 |
|
10 | 10 | ```csharp |
11 | | -using Flint; |
| 11 | +using STYME; |
12 | 12 |
|
13 | | -var matcher = new TextMatcher(new[] { "cat", "dog" }); |
14 | | -var results = matcher.Find("The dog chased the cat."); |
15 | | - |
16 | | -foreach (var match in results) |
17 | | -{ |
18 | | - Console.WriteLine($"Found '{match.Value}' at index {match.StartIndex}."); |
19 | | -} |
20 | | -``` |
21 | | - |
22 | | -Output: |
23 | | - |
24 | | -``` |
25 | | -Found 'dog' at index 4. |
26 | | -Found 'cat' at index 19. |
27 | | -``` |
28 | | - |
29 | | -An optional ```StringComparison``` can be passed to ```TextMatcher```, influencing it's behavior when performing searches/replacement. |
30 | | -If none is given, it will default to ```StringComparison.CurrentCulture```. |
31 | | - |
32 | | -```csharp |
33 | | -using Flint; |
34 | | - |
35 | | -var matcher = new TextMatcher(new[] { "cat", "dog" }, StringComparison.OrdinalIgnoreCase); |
36 | | -var results = matcher.Find("The DOG chased the CaT."); |
37 | | - |
38 | | -foreach (var match in results) |
39 | | -{ |
40 | | - Console.WriteLine($"Found '{match.Value}' at index {match.StartIndex}."); |
41 | | -} |
| 13 | +// By default NaturalDateTime uses the current system time as the base |
| 14 | +var parser = new NaturalDateTime(); |
| 15 | +var result = parser.Parse("add 2 days"); |
| 16 | +Console.WriteLine(result); |
42 | 17 | ``` |
43 | 18 |
|
44 | | -Output: |
| 19 | +Output (example): |
45 | 20 |
|
46 | 21 | ``` |
47 | | -Found 'DOG' at index 4. |
48 | | -Found 'CaT' at index 19. |
| 22 | +2025-10-04T14:23:00 |
49 | 23 | ``` |
50 | 24 |
|
51 | | -### Find with a callback |
52 | | - |
53 | | -`TextMatcher` provides an overload of `Find` that accepts a callback invoked for each match. This can be convenient to process matches as they are found without allocating a results collection. |
| 25 | +You can also parse expressions relative to a specific `DateTime` using `NaturalDateTime.From`. |
54 | 26 |
|
55 | 27 | ```csharp |
56 | | -using Flint; |
| 28 | +using STYME; |
57 | 29 |
|
58 | | -var matcher = new TextMatcher(new[] { "cat", "dog" }); |
59 | | -matcher.Find("The dog chased the cat.", match => |
60 | | -{ |
61 | | - Console.WriteLine($"Found '{match.Value}' at index {match.StartIndex}."); |
62 | | -}); |
| 30 | +var baseTime = new DateTime(2020, 1, 1, 0, 0, 0); |
| 31 | +var parser = NaturalDateTime.From(baseTime); |
| 32 | +var result = parser.Parse("add 1 month"); |
| 33 | +Console.WriteLine(result); |
63 | 34 | ``` |
64 | 35 |
|
65 | 36 | Output: |
66 | 37 |
|
67 | 38 | ``` |
68 | | -Found 'dog' at index 4. |
69 | | -Found 'cat' at index 19. |
70 | | -``` |
71 | | - |
72 | | -### Scan multiple texts with `FindAll` |
73 | | - |
74 | | -```csharp |
75 | | -using Flint; |
76 | | - |
77 | | -var patterns = new[] { "cat", "dog", "bird" }; |
78 | | -var matcher = new TextMatcher(patterns); |
79 | | - |
80 | | -var texts = new[] |
81 | | -{ |
82 | | - "The dog chased the cat.", |
83 | | - "A bird watched them from above.", |
84 | | - "No animals here." |
85 | | -}; |
86 | | - |
87 | | -var all = matcher.FindAll(texts); |
88 | | - |
89 | | -foreach (var kv in all) |
90 | | -{ |
91 | | - Console.WriteLine(kv.Key); |
92 | | - foreach (var m in kv.Value) |
93 | | - { |
94 | | - Console.WriteLine($"Found '{m.Value}' at index {m.StartIndex}."); |
95 | | - } |
96 | | -} |
97 | | -``` |
98 | | - |
99 | | -Example output: |
100 | | - |
101 | | -``` |
102 | | -The dog chased the cat. |
103 | | -Found 'cat' at index 19. |
104 | | -Found 'dog' at index 4. |
105 | | -A bird watched them from above. |
106 | | -Found 'bird' at index 2. |
107 | | -No animals here. |
108 | | -``` |
109 | | - |
110 | | -If you need to scan many input strings and handle matches as they are discovered, use the `FindAll` overload that accepts an `Action<string, Match>` callback. The callback receives the original text and the match. |
111 | | - |
112 | | -```csharp |
113 | | -using Flint; |
114 | | - |
115 | | -var patterns = new[] { "cat", "dog" }; |
116 | | -var matcher = new TextMatcher(patterns); |
117 | | - |
118 | | -var texts = new[] |
119 | | -{ |
120 | | - "The dog chased the cat.", |
121 | | - "A bird watched from above.", |
122 | | -}; |
123 | | - |
124 | | -matcher.FindAll(texts, (text, match) => |
125 | | -{ |
126 | | - Console.WriteLine(text); |
127 | | - Console.WriteLine($"Found '{match.Value}' at index {match.StartIndex}."); |
128 | | -}); |
129 | | -``` |
130 | | - |
131 | | -Example output: |
132 | | - |
133 | | -``` |
134 | | -The dog chased the cat. |
135 | | -Found 'dog' at index 4. |
136 | | -Found 'cat' at index 19. |
137 | | -A bird watched from above. |
138 | | -``` |
139 | | - |
140 | | -### Exact (whole-word) matching with `MatchMode` |
141 | | - |
142 | | -The `MatchMode` enum lets you require matches to be whole words. This prevents short patterns from matching inside longer words (for example, preventing "he" from matching inside "she"). |
143 | | - |
144 | | -```csharp |
145 | | -using Flint; |
146 | | - |
147 | | -var patterns = new[] { "he", "she" }; |
148 | | -var matcher = new TextMatcher(patterns, StringComparison.CurrentCulture, MatchMode.ExactMatch); |
149 | | - |
150 | | -var results = matcher.Find("she sells seashells."); |
151 | | - |
152 | | -foreach (var m in results) |
153 | | -{ |
154 | | - Console.WriteLine($"Found '{m.Value}' at index {m.StartIndex}."); |
155 | | -} |
156 | | -``` |
157 | | - |
158 | | -Output (only the whole-word `she` is matched): |
159 | | - |
160 | | -``` |
161 | | -Found 'she' at index 0. |
| 39 | +2020-02-01T00:00:00 |
162 | 40 | ``` |
163 | 41 |
|
164 | | -### Overlapping and nested matches |
| 42 | +### Deduct / Subtract |
165 | 43 |
|
166 | | -```csharp |
167 | | -using Flint; |
168 | | - |
169 | | -var matcher = new TextMatcher(new[] { "he", "she", "his", "hers" }); |
170 | | -var results = matcher.Find("she is his hero"); |
171 | | - |
172 | | -foreach (var m in results) |
173 | | -{ |
174 | | - Console.WriteLine($"Found '{m.Value}' at index {m.StartIndex}."); |
175 | | -} |
176 | | -``` |
177 | | - |
178 | | -Example output: |
179 | | - |
180 | | -``` |
181 | | -Found 'she' at index 0. |
182 | | -Found 'he' at index 1. |
183 | | -Found 'his' at index 7. |
184 | | -Found 'he' at index 10. |
185 | | -``` |
186 | | - |
187 | | -### Replace all matches with a single value |
| 44 | +You can subtract time using the `deduct` (or `subtract`) keyword. The expression works the same as `add` but shifts the base time backwards. |
188 | 45 |
|
189 | 46 | ```csharp |
190 | | -using Flint; |
| 47 | +using STYME; |
191 | 48 |
|
192 | | -var matcher = new TextMatcher(new[] { "cat", "dog" }); |
193 | | -var replaced = matcher.Replace("The dog chased the cat.", "animal"); |
194 | | -Console.WriteLine(replaced); |
| 49 | +var baseTime = new DateTime(2020, 3, 1, 12, 0, 0); |
| 50 | +var parser = NaturalDateTime.From(baseTime); |
| 51 | +var result = parser.Parse("deduct 1 month"); |
| 52 | +Console.WriteLine(result); |
195 | 53 | ``` |
196 | 54 |
|
197 | 55 | Output: |
198 | 56 |
|
199 | 57 | ``` |
200 | | -The animal chased the animal. |
| 58 | +2020-02-01T12:00:00 |
201 | 59 | ``` |
202 | 60 |
|
203 | | -### Replace matches using a function |
| 61 | +### Supported units with `add` and `deduct` |
204 | 62 |
|
205 | | -```csharp |
206 | | -using Flint; |
| 63 | +The `add` and `deduct` expressions support the following units (singular and plural forms): |
207 | 64 |
|
208 | | -var matcher = new TextMatcher(new[] { "cat", "dog" }); |
209 | | -var replaced = matcher.Replace( |
210 | | - "The dog chased the cat.", |
211 | | - match => match.Value.ToUpper() |
212 | | -); |
213 | | -Console.WriteLine(replaced); |
214 | | -``` |
215 | | - |
216 | | -Output: |
217 | | - |
218 | | -``` |
219 | | -The DOG chased the CAT. |
220 | | -``` |
| 65 | +- `second`, `seconds` |
| 66 | +- `minute`, `minutes` |
| 67 | +- `hour`, `hours` |
| 68 | +- `day`, `days` |
| 69 | +- `week`, `weeks` |
| 70 | +- `month`, `months` |
| 71 | +- `year`, `years` |
| 72 | +- `decade`, `decades` |
| 73 | +- `century`, `centuries` |
| 74 | +- `millennium`, `millennia` |
221 | 75 |
|
222 | | -### Replace each pattern with a different value |
| 76 | +Example: |
223 | 77 |
|
224 | 78 | ```csharp |
225 | | -using Flint; |
226 | | - |
227 | | -var matcher = new TextMatcher(new[] { "cat", "dog" }); |
228 | | -var replaced = matcher.Replace( |
229 | | - "The dog chased the cat.", |
230 | | - new[] { "feline", "canine" } |
231 | | -); |
232 | | -Console.WriteLine(replaced); |
233 | | -``` |
234 | | - |
235 | | -Output: |
236 | | - |
237 | | -``` |
238 | | -The canine chased the feline. |
239 | | -``` |
240 | | - |
241 | | -### Load patterns from a file |
242 | | - |
243 | | -```csharp |
244 | | -using Flint; |
245 | | - |
246 | | -var patterns = File.ReadAllLines("patterns.txt") |
247 | | - .Where(l => !string.IsNullOrWhiteSpace(l)); |
248 | | - |
249 | | -var matcher = new TextMatcher(patterns); |
250 | | - |
251 | | -var text = File.ReadAllText("large_input.txt"); |
252 | | -var hits = matcher.Find(text).ToArray(); |
| 79 | +using STYME; |
253 | 80 |
|
254 | | -Console.WriteLine($"Found {hits.Length} matches."); |
| 81 | +var parser = NaturalDateTime.From(new DateTime(2000, 1, 1)); |
| 82 | +Console.WriteLine(parser.Parse("add 2 decades")); // 2020-01-01 |
| 83 | +Console.WriteLine(parser.Parse("deduct 1 century")); // 1900-01-01 |
255 | 84 | ``` |
256 | 85 |
|
257 | 86 | ## Support |
258 | 87 |
|
259 | | -* .NET Standard 2.0 and .NET 8.0 |
| 88 | +* .NET 8.0 and later |
260 | 89 |
|
261 | 90 | ## License |
262 | 91 |
|
263 | | -Flint is licensed under the [MIT License](LICENSE). |
| 92 | +STYME is licensed under the [MIT License](LICENSE). |
0 commit comments