* RegexTest.cs: Add some simple tests for debugging/zen
[mono.git] / mcs / class / System / System.Text.RegularExpressions / regex.cs
1 //\r
2 // assembly:    System\r
3 // namespace:   System.Text.RegularExpressions\r
4 // file:        regex.cs\r
5 //\r
6 // author:      Dan Lewis (dlewis@gmx.co.uk)\r
7 //              (c) 2002\r
8
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29 \r
30 using System;\r
31 using System.Text;\r
32 using System.Collections;\r
33 using System.Reflection;\r
34 using System.Reflection.Emit;\r
35 using System.Runtime.Serialization;\r
36 \r
37 using RegularExpression = System.Text.RegularExpressions.Syntax.RegularExpression;\r
38 using Parser = System.Text.RegularExpressions.Syntax.Parser;\r
39 \r
40 using System.Diagnostics;\r
41 \r
42 \r
43 namespace System.Text.RegularExpressions {\r
44         \r
45         public delegate string MatchEvaluator (Match match);\r
46 \r
47         [Flags]\r
48         public enum RegexOptions {\r
49                 None                            = 0x000,\r
50                 IgnoreCase                      = 0x001,\r
51                 Multiline                       = 0x002,\r
52                 ExplicitCapture                 = 0x004,\r
53                 Compiled                        = 0x008,\r
54                 Singleline                      = 0x010,\r
55                 IgnorePatternWhitespace         = 0x020,\r
56                 RightToLeft                     = 0x040,\r
57                 ECMAScript                      = 0x100,\r
58                 CultureInvariant                = 0x200 \r
59         }\r
60         \r
61         [Serializable]\r
62         public class Regex : ISerializable {\r
63                 public static void CompileToAssembly\r
64                         (RegexCompilationInfo[] regexes, AssemblyName aname)\r
65                 {\r
66                                 Regex.CompileToAssembly(regexes, aname, new CustomAttributeBuilder[] {}, null);\r
67                 }\r
68 \r
69                 public static void CompileToAssembly\r
70                         (RegexCompilationInfo[] regexes, AssemblyName aname,\r
71                          CustomAttributeBuilder[] attribs)\r
72                 {\r
73                         Regex.CompileToAssembly(regexes, aname, attribs, null);\r
74                 }\r
75 \r
76                 [MonoTODO]\r
77                 public static void CompileToAssembly\r
78                         (RegexCompilationInfo[] regexes, AssemblyName aname,\r
79                          CustomAttributeBuilder[] attribs, string resourceFile)\r
80                 {\r
81                         throw new NotImplementedException ();\r
82                         // TODO : Make use of attribs and resourceFile parameters\r
83                         /*\r
84                         AssemblyBuilder asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly (aname, AssemblyBuilderAccess.RunAndSave);\r
85                         ModuleBuilder modBuilder = asmBuilder.DefineDynamicModule("InnerRegexModule",aname.Name);\r
86                         Parser psr = new Parser ();     \r
87                         \r
88                         System.Console.WriteLine("CompileToAssembly");\r
89                                \r
90                         for(int i=0; i < regexes.Length; i++)\r
91                                 {\r
92                                         System.Console.WriteLine("Compiling expression :" + regexes[i].Pattern);\r
93                                         RegularExpression re = psr.ParseRegularExpression (regexes[i].Pattern, regexes[i].Options);\r
94                                         \r
95                                         // compile\r
96                                                                                 \r
97                                         CILCompiler cmp = new CILCompiler (modBuilder, i);\r
98                                         bool reverse = (regexes[i].Options & RegexOptions.RightToLeft) !=0;\r
99                                         re.Compile (cmp, reverse);\r
100                                         cmp.Close();\r
101                                         \r
102                                 }\r
103                        \r
104 \r
105                         // Define a runtime class with specified name and attributes.\r
106                         TypeBuilder builder = modBuilder.DefineType("ITest");\r
107                         builder.CreateType();\r
108                         asmBuilder.Save(aname.Name);\r
109                         */\r
110                 }\r
111                 \r
112                 public static string Escape (string str) {\r
113                         return Parser.Escape (str);\r
114                 }\r
115 \r
116                 public static string Unescape (string str) {\r
117                         return Parser.Unescape (str);\r
118                 }\r
119 \r
120                 public static bool IsMatch (string input, string pattern) {\r
121                         return IsMatch (input, pattern, RegexOptions.None);\r
122                 }\r
123 \r
124                 public static bool IsMatch (string input, string pattern, RegexOptions options) {\r
125                         Regex re = new Regex (pattern, options);\r
126                         return re.IsMatch (input);\r
127                 }\r
128 \r
129                 public static Match Match (string input, string pattern) {\r
130                         return Regex.Match (input, pattern, RegexOptions.None);\r
131                 }\r
132 \r
133                 public static Match Match (string input, string pattern, RegexOptions options) {\r
134                         Regex re = new Regex (pattern, options);\r
135                         return re.Match (input);\r
136                 }\r
137 \r
138                 public static MatchCollection Matches (string input, string pattern) {\r
139                         return Matches (input, pattern, RegexOptions.None);\r
140                 }\r
141 \r
142                 public static MatchCollection Matches (string input, string pattern, RegexOptions options) {\r
143                         Regex re = new Regex (pattern, options);\r
144                         return re.Matches (input);\r
145                 }\r
146 \r
147                 public static string Replace\r
148                         (string input, string pattern, MatchEvaluator evaluator)\r
149                 {\r
150                         return Regex.Replace (input, pattern, evaluator, RegexOptions.None);\r
151                 }\r
152 \r
153                 public static string Replace\r
154                         (string input, string pattern, MatchEvaluator evaluator,\r
155                          RegexOptions options)\r
156                 {\r
157                         Regex re = new Regex (pattern, options);\r
158                         return re.Replace (input, evaluator);\r
159                 }\r
160 \r
161                 public static string Replace\r
162                         (string input, string pattern, string replacement)\r
163                 {\r
164                         return Regex.Replace (input, pattern, replacement, RegexOptions.None);\r
165                 }\r
166 \r
167                 public static string Replace\r
168                         (string input, string pattern, string replacement,\r
169                          RegexOptions options)\r
170                 {\r
171                         Regex re = new Regex (pattern, options);\r
172                         return re.Replace (input, replacement);\r
173                 }\r
174 \r
175                 public static string[] Split (string input, string pattern) {\r
176                         return Regex.Split (input, pattern, RegexOptions.None);\r
177                 }\r
178 \r
179                 public static string[] Split (string input, string pattern, RegexOptions options) {\r
180                         Regex re = new Regex (pattern, options);\r
181                         return re.Split (input);\r
182                 }\r
183 \r
184                 // private\r
185 \r
186                 private static FactoryCache cache = new FactoryCache (200);     // TODO put some meaningful number here\r
187 \r
188                 // constructors\r
189 \r
190                 protected Regex () {\r
191                         // XXX what's this constructor for?\r
192                         // : Used to compile to assembly (Custum regex inherit from Regex and use this constructor)\r
193                 }\r
194 \r
195                 public Regex (string pattern) : this (pattern, RegexOptions.None) {\r
196                 }\r
197 \r
198                 public Regex (string pattern, RegexOptions options) {\r
199                         this.pattern = pattern;\r
200                         this.roptions = options;\r
201                 \r
202                         this.machineFactory = cache.Lookup (pattern, options);\r
203 \r
204                         if (this.machineFactory == null) {\r
205                                 // parse and install group mapping\r
206 \r
207                                 Parser psr = new Parser ();\r
208                                 RegularExpression re = psr.ParseRegularExpression (pattern, options);\r
209                                 this.group_count = re.GroupCount;\r
210                                 this.mapping = psr.GetMapping ();\r
211 \r
212                                 // compile\r
213                                 \r
214                                 ICompiler cmp;\r
215                                 //if ((options & RegexOptions.Compiled) != 0)\r
216                                 //      //throw new Exception ("Not implemented.");\r
217                                 //      cmp = new CILCompiler ();\r
218                                 //else\r
219                                         cmp = new PatternCompiler ();\r
220 \r
221                                 re.Compile (cmp, RightToLeft);\r
222 \r
223                                 // install machine factory and add to pattern cache\r
224 \r
225                                 this.machineFactory = cmp.GetMachineFactory ();\r
226                                 this.machineFactory.Mapping = mapping;\r
227                                 cache.Add (pattern, options, this.machineFactory);\r
228                         } else {\r
229                                 this.group_count = this.machineFactory.GroupCount;\r
230                                 this.mapping = this.machineFactory.Mapping;\r
231                         }\r
232                 }\r
233 \r
234                 private Regex (SerializationInfo info, StreamingContext context) :\r
235                         this (info.GetString ("pattern"), \r
236                               (RegexOptions) info.GetValue ("options", typeof (RegexOptions))) {\r
237                 }\r
238 \r
239                 // fixes public API signature\r
240                 ~Regex ()\r
241                 {\r
242                 }\r
243 \r
244                 // public instance properties\r
245                 \r
246                 public RegexOptions Options {\r
247                         get { return roptions; }\r
248                 }\r
249 \r
250                 public bool RightToLeft {\r
251                         get { return (roptions & RegexOptions.RightToLeft) != 0; }\r
252                 }\r
253 \r
254                 // public instance methods\r
255                 \r
256                 public string[] GetGroupNames () {\r
257                         string[] names = new string[mapping.Count];\r
258                         mapping.Keys.CopyTo (names, 0);\r
259 \r
260                         return names;\r
261                 }\r
262 \r
263                 public int[] GetGroupNumbers () {\r
264                         int[] numbers = new int[mapping.Count];\r
265                         mapping.Values.CopyTo (numbers, 0);\r
266 \r
267                         return numbers;\r
268                 }\r
269 \r
270                 public string GroupNameFromNumber (int i) {\r
271                         if (i > group_count)\r
272                                 return "";\r
273                 \r
274                         foreach (string name in mapping.Keys) {\r
275                                 if ((int)mapping[name] == i)\r
276                                         return name;\r
277                         }\r
278 \r
279                         return "";\r
280                 }\r
281 \r
282                 public int GroupNumberFromName (string name) {\r
283                         if (mapping.Contains (name))\r
284                                 return (int)mapping[name];\r
285 \r
286                         return -1;\r
287                 }\r
288 \r
289                 // match methods\r
290                 \r
291                 public bool IsMatch (string input) {\r
292                         if (RightToLeft)\r
293                                 return IsMatch (input, input.Length);\r
294                         else\r
295                                 return IsMatch (input, 0);\r
296                 }\r
297 \r
298                 public bool IsMatch (string input, int startat) {\r
299                         return Match (input, startat).Success;\r
300                 }\r
301 \r
302                 public Match Match (string input) {\r
303                         if (RightToLeft)\r
304                                 return Match (input, input.Length);\r
305                         else\r
306                                 return Match (input, 0);\r
307                 }\r
308 \r
309                 public Match Match (string input, int startat) {\r
310         \r
311                         return CreateMachine ().Scan (this, input, startat, input.Length);\r
312                 }\r
313 \r
314                 public Match Match (string input, int startat, int length) {\r
315         \r
316                         return CreateMachine ().Scan (this, input, startat, startat + length);\r
317                 }\r
318 \r
319                 public MatchCollection Matches (string input) {\r
320                         if (RightToLeft)\r
321                                 return Matches (input, input.Length);\r
322                         else\r
323                                 return Matches (input, 0);\r
324                 }\r
325 \r
326                 public MatchCollection Matches (string input, int startat) {\r
327                         MatchCollection ms = new MatchCollection ();\r
328                         Match m = Match (input, startat);\r
329                         while (m.Success) {\r
330                                 ms.Add (m);\r
331                                 m = m.NextMatch ();\r
332                         }\r
333 \r
334                         return ms;\r
335                 }\r
336 \r
337                 // replace methods\r
338 \r
339                 public string Replace (string input, MatchEvaluator evaluator) {\r
340                         if (RightToLeft)                        \r
341                                 return Replace (input, evaluator, Int32.MaxValue, input.Length);\r
342                         else\r
343                                 return Replace (input, evaluator, Int32.MaxValue, 0);\r
344                 }\r
345 \r
346                 public string Replace (string input, MatchEvaluator evaluator, int count) {\r
347                         if (RightToLeft)\r
348                                 return Replace (input, evaluator, count, input.Length);\r
349                         else\r
350                                 return Replace (input, evaluator, count, 0);\r
351                 }\r
352 \r
353                 public string Replace (string input, MatchEvaluator evaluator, int count, int startat)\r
354                 {\r
355                         StringBuilder result = new StringBuilder ();\r
356                         int ptr = startat;\r
357 \r
358                         Match m = Match (input, startat);\r
359                         while (m.Success && count -- > 0) {\r
360                                 result.Append (input.Substring (ptr, m.Index - ptr));\r
361                                 result.Append (evaluator (m));\r
362 \r
363                                 ptr = m.Index + m.Length;\r
364                                 m = m.NextMatch ();\r
365                         }\r
366                         result.Append (input.Substring (ptr));\r
367 \r
368                         return result.ToString ();\r
369                 }\r
370 \r
371                 public string Replace (string input, string replacement) {\r
372                         if (RightToLeft)\r
373                                 return Replace (input, replacement, Int32.MaxValue, input.Length);\r
374                         else\r
375                                 return Replace (input, replacement, Int32.MaxValue, 0);\r
376                 }\r
377 \r
378                 public string Replace (string input, string replacement, int count) {\r
379                         if (RightToLeft)                        \r
380                                 return Replace (input, replacement, count, input.Length);\r
381                         else    \r
382                                 return Replace (input, replacement, count, 0);\r
383                 }\r
384 \r
385                 public string Replace (string input, string replacement, int count, int startat) {\r
386                         ReplacementEvaluator ev = new ReplacementEvaluator (this, replacement);\r
387                         return Replace (input, new MatchEvaluator (ev.Evaluate), count, startat);\r
388                 }\r
389 \r
390                 // split methods\r
391 \r
392                 public string[] Split (string input) {\r
393                         if (RightToLeft)        \r
394                                 return Split (input, Int32.MaxValue, input.Length);\r
395                         else\r
396                                 return Split (input, Int32.MaxValue, 0);\r
397                 }\r
398 \r
399                 public string[] Split (string input, int count) {\r
400                         if (RightToLeft)                                \r
401                                 return Split (input, count, input.Length);\r
402                         else\r
403                                 return Split (input, count, 0);\r
404                 }\r
405 \r
406                 public string[] Split (string input, int count, int startat) {\r
407                         ArrayList splits = new ArrayList ();\r
408                         if (count == 0)\r
409                                 count = Int32.MaxValue;\r
410 \r
411                         int ptr = startat;\r
412                         while (--count > 0) {\r
413                                 Match m = Match (input, ptr);\r
414                                 if (!m.Success)\r
415                                         break;\r
416                         \r
417                                 if (RightToLeft)\r
418                                         splits.Add (input.Substring (m.Index + m.Length , ptr - m.Index - m.Length ));\r
419                                 else\r
420                                         splits.Add (input.Substring (ptr, m.Index - ptr));\r
421                                         \r
422                                 int gcount = m.Groups.Count;\r
423                                 for (int gindex = 1; gindex < gcount; gindex++) {\r
424                                         Group grp = m.Groups [gindex];\r
425                                         splits.Add (input.Substring (grp.Index, grp.Length));\r
426                                 }\r
427 \r
428                                 if (RightToLeft)\r
429                                         ptr = m.Index; \r
430                                 else\r
431                                         ptr = m.Index + m.Length;\r
432                                         \r
433                         }\r
434 \r
435                         if (RightToLeft) {\r
436                                 if ( ptr >= 0) {\r
437                                                 splits.Add (input.Substring(0, ptr));\r
438                                 }\r
439                         }                               \r
440                         else {\r
441                                 if (ptr <= input.Length) {\r
442                                                 splits.Add (input.Substring (ptr));\r
443                                 }\r
444                                 \r
445                         }\r
446 \r
447                         return (string []) splits.ToArray (typeof (string));\r
448                 }\r
449 \r
450                 // MS undocummented method\r
451                 [MonoTODO]\r
452                 protected void InitializeReferences() {\r
453                         throw new NotImplementedException ();\r
454                 }\r
455 \r
456                 [MonoTODO]\r
457                 protected bool UseOptionC(){\r
458                         throw new NotImplementedException ();\r
459                 }\r
460 \r
461                 [MonoTODO]\r
462                 protected bool UseOptionR(){\r
463                         throw new NotImplementedException ();\r
464                 }\r
465 \r
466                 // object methods\r
467                 \r
468                 public override string ToString () {\r
469                         return pattern;\r
470                 }\r
471 \r
472                 // ISerializable interface\r
473                 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context) {\r
474                         info.AddValue ("pattern", this.ToString (), typeof (string));\r
475                         info.AddValue ("options", this.Options, typeof (RegexOptions));\r
476                 }\r
477 \r
478                 // internal\r
479 \r
480                 internal int GroupCount {\r
481                         get { return group_count; }\r
482                 }\r
483 \r
484                 // private\r
485 \r
486                 private IMachine CreateMachine () {\r
487                         return machineFactory.NewInstance ();\r
488                 }\r
489 \r
490                 private IMachineFactory machineFactory;\r
491                 private IDictionary mapping;\r
492                 private int group_count;\r
493 \r
494                 \r
495                 // protected members\r
496 \r
497                 protected internal string pattern;\r
498                 protected internal RegexOptions roptions;\r
499                 \r
500                 // MS undocumented members\r
501                 [MonoTODO]\r
502                 protected internal System.Collections.Hashtable capnames;\r
503                 [MonoTODO]\r
504                 protected internal System.Collections.Hashtable caps;\r
505                 [MonoTODO]\r
506                 protected internal int capsize;\r
507                 [MonoTODO]\r
508                 protected internal string[] capslist;\r
509                 [MonoTODO]\r
510                 protected internal RegexRunnerFactory factory;\r
511         }\r
512 \r
513         [Serializable]\r
514         public class RegexCompilationInfo {\r
515                 public RegexCompilationInfo (string pattern, RegexOptions options, string name, string nspace, bool isPublic)\r
516                 {\r
517                         this.pattern = pattern;\r
518                         this.options = options;\r
519                         this.name = name;\r
520                         this.nspace = nspace;\r
521                         this.isPublic = isPublic;\r
522                 }\r
523 \r
524                 public bool IsPublic {\r
525                         get { return isPublic; }\r
526                         set { isPublic = value; }\r
527                 }\r
528 \r
529                 public string Name {\r
530                         get { return name; }\r
531                         set { name = value; }\r
532                 }\r
533 \r
534                 public string Namespace {\r
535                         get { return nspace; }\r
536                         set { nspace = value; }\r
537                 }\r
538 \r
539                 public RegexOptions Options {\r
540                         get { return options; }\r
541                         set { options = value; }\r
542                 }\r
543 \r
544                 public string Pattern {\r
545                         get { return pattern; }\r
546                         set { pattern = value; }\r
547                 }\r
548 \r
549                 // private\r
550 \r
551                 private string pattern, name, nspace;\r
552                 private RegexOptions options;\r
553                 private bool isPublic;\r
554         }\r
555 }\r