3 // namespace: System.Text.RegularExpressions
6 // author: Dan Lewis (dlewis@gmx.co.uk)
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:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
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.
32 using System.Collections;
33 using System.Reflection;
35 using System.Reflection.Emit;
37 using System.Runtime.Serialization;
39 using RegularExpression = System.Text.RegularExpressions.Syntax.RegularExpression;
40 using Parser = System.Text.RegularExpressions.Syntax.Parser;
42 using System.Diagnostics;
45 namespace System.Text.RegularExpressions {
48 public partial class Regex : ISerializable {
52 public static void CompileToAssembly (RegexCompilationInfo [] regexes, AssemblyName aname)
54 Regex.CompileToAssembly(regexes, aname, new CustomAttributeBuilder [] {}, null);
58 public static void CompileToAssembly (RegexCompilationInfo [] regexes, AssemblyName aname,
59 CustomAttributeBuilder [] attribs)
61 Regex.CompileToAssembly(regexes, aname, attribs, null);
65 public static void CompileToAssembly (RegexCompilationInfo [] regexes, AssemblyName aname,
66 CustomAttributeBuilder [] attribs, string resourceFile)
68 throw new NotImplementedException ();
69 // TODO : Make use of attribs and resourceFile parameters
71 AssemblyBuilder asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly (aname, AssemblyBuilderAccess.RunAndSave);
72 ModuleBuilder modBuilder = asmBuilder.DefineDynamicModule("InnerRegexModule",aname.Name);
73 Parser psr = new Parser ();
75 System.Console.WriteLine("CompileToAssembly");
77 for(int i=0; i < regexes.Length; i++)
79 System.Console.WriteLine("Compiling expression :" + regexes[i].Pattern);
80 RegularExpression re = psr.ParseRegularExpression (regexes[i].Pattern, regexes[i].Options);
84 CILCompiler cmp = new CILCompiler (modBuilder, i);
85 bool reverse = (regexes[i].Options & RegexOptions.RightToLeft) !=0;
86 re.Compile (cmp, reverse);
92 // Define a runtime class with specified name and attributes.
93 TypeBuilder builder = modBuilder.DefineType("ITest");
95 asmBuilder.Save(aname.Name);
100 public static string Escape (string str)
103 throw new ArgumentNullException ("str");
104 return Parser.Escape (str);
107 public static string Unescape (string str)
110 throw new ArgumentNullException ("str");
111 return Parser.Unescape (str);
114 public static bool IsMatch (string input, string pattern)
116 return IsMatch (input, pattern, RegexOptions.None);
119 public static bool IsMatch (string input, string pattern, RegexOptions options)
121 Regex re = new Regex (pattern, options);
122 return re.IsMatch (input);
125 public static Match Match (string input, string pattern)
127 return Regex.Match (input, pattern, RegexOptions.None);
130 public static Match Match (string input, string pattern, RegexOptions options)
132 Regex re = new Regex (pattern, options);
133 return re.Match (input);
136 public static MatchCollection Matches (string input, string pattern)
138 return Matches (input, pattern, RegexOptions.None);
141 public static MatchCollection Matches (string input, string pattern, RegexOptions options)
143 Regex re = new Regex (pattern, options);
144 return re.Matches (input);
147 public static string Replace (string input, string pattern, MatchEvaluator evaluator)
149 return Regex.Replace (input, pattern, evaluator, RegexOptions.None);
152 public static string Replace (string input, string pattern, MatchEvaluator evaluator,
153 RegexOptions options)
155 Regex re = new Regex (pattern, options);
156 return re.Replace (input, evaluator);
159 public static string Replace (string input, string pattern, string replacement)
161 return Regex.Replace (input, pattern, replacement, RegexOptions.None);
164 public static string Replace (string input, string pattern, string replacement,
165 RegexOptions options)
167 Regex re = new Regex (pattern, options);
168 return re.Replace (input, replacement);
171 public static string [] Split (string input, string pattern)
173 return Regex.Split (input, pattern, RegexOptions.None);
176 public static string [] Split (string input, string pattern, RegexOptions options)
178 Regex re = new Regex (pattern, options);
179 return re.Split (input);
182 static FactoryCache cache = new FactoryCache (15);
183 public static int CacheSize {
184 get { return cache.Capacity; }
187 throw new ArgumentOutOfRangeException ("CacheSize");
189 cache.Capacity = value;
198 // This constructor is used by compiled regular expressions that are
199 // classes derived from Regex class. No initialization required.
204 public Regex (string pattern) : this (pattern, RegexOptions.None)
208 public Regex (string pattern, RegexOptions options)
211 throw new ArgumentNullException ("pattern");
212 validate_options (options);
213 this.pattern = pattern;
214 this.roptions = options;
219 [MonoTODO ("Timeouts are ignored.")]
220 public Regex (string pattern, RegexOptions options, TimeSpan matchTimeout)
221 : this (pattern, options)
223 MatchTimeout = matchTimeout;
226 [MonoTODO ("Timeouts are ignored.")]
227 public TimeSpan MatchTimeout {
232 [MonoTODO ("Timeouts are ignored.")]
233 public static bool IsMatch (
234 string input, string pattern, RegexOptions options, TimeSpan matchTimeout)
236 return IsMatch (input, pattern, options);
239 [MonoTODO ("Timeouts are ignored.")]
240 public static Match Match (
241 string input, string pattern, RegexOptions options, TimeSpan matchTimeout)
243 return Match (input, pattern, options);
246 [MonoTODO ("Timeouts are ignored.")]
247 public static MatchCollection Matches (
248 string input, string pattern, RegexOptions options, TimeSpan matchTimeout)
250 return Matches (input, pattern, options, matchTimeout);
253 [MonoTODO ("Timeouts are ignored.")]
254 public static string Replace (
255 string input, string pattern, string replacement, RegexOptions options,
256 TimeSpan matchTimeout)
258 return Replace (input, pattern, replacement, options);
261 [MonoTODO ("Timeouts are ignored.")]
262 public static string Replace (
263 string input, string pattern, MatchEvaluator evaluator, RegexOptions options,
264 TimeSpan matchTimeout)
266 return Replace (input, pattern, evaluator, options);
269 [MonoTODO ("Timeouts are ignored.")]
270 public static string[] Split (
271 string input, string pattern, RegexOptions options, TimeSpan matchTimeout)
273 return Split (input, pattern, options);
276 public static readonly TimeSpan InfiniteMatchTimeout = TimeSpan.FromMilliseconds (-1);
279 static void validate_options (RegexOptions options)
281 const RegexOptions allopts =
283 RegexOptions.IgnoreCase |
284 RegexOptions.Multiline |
285 RegexOptions.ExplicitCapture |
286 #if MOBILE || !NET_2_1
287 RegexOptions.Compiled |
289 RegexOptions.Singleline |
290 RegexOptions.IgnorePatternWhitespace |
291 RegexOptions.RightToLeft |
292 RegexOptions.ECMAScript |
293 RegexOptions.CultureInvariant;
295 const RegexOptions ecmaopts =
296 RegexOptions.IgnoreCase |
297 RegexOptions.Multiline |
298 #if MOBILE || !NET_2_1
299 RegexOptions.Compiled |
301 RegexOptions.ECMAScript;
303 if ((options & ~allopts) != 0)
304 throw new ArgumentOutOfRangeException ("options");
305 if ((options & RegexOptions.ECMAScript) != 0 && (options & ~ecmaopts) != 0)
306 throw new ArgumentOutOfRangeException ("options");
311 this.machineFactory = cache.Lookup (this.pattern, this.roptions);
313 if (this.machineFactory == null) {
316 this.group_count = this.machineFactory.GroupCount;
317 this.gap = this.machineFactory.Gap;
318 this.mapping = this.machineFactory.Mapping;
319 this.group_names = this.machineFactory.NamesMapping;
323 private void InitNewRegex ()
325 this.machineFactory = CreateMachineFactory (this.pattern, this.roptions);
326 cache.Add (this.pattern, this.roptions, this.machineFactory);
327 this.group_count = machineFactory.GroupCount;
328 this.gap = this.machineFactory.Gap;
329 this.mapping = machineFactory.Mapping;
330 this.group_names = this.machineFactory.NamesMapping;
334 // The new rx engine seems to be working now, but
335 // potential problems are being tracked down here:
336 // https://bugzilla.novell.com/show_bug.cgi?id=470827
337 static readonly bool old_rx =
338 Environment.GetEnvironmentVariable ("MONO_NEW_RX") == null;
341 private static IMachineFactory CreateMachineFactory (string pattern, RegexOptions options)
343 Parser psr = new Parser ();
344 RegularExpression re = psr.ParseRegularExpression (pattern, options);
347 ICompiler cmp = new PatternCompiler ();
351 if ((options & RegexOptions.Compiled) != 0)
352 cmp = new CILCompiler ();
354 cmp = new RxCompiler ();
356 cmp = new PatternCompiler ();
360 re.Compile (cmp, (options & RegexOptions.RightToLeft) != 0);
362 IMachineFactory machineFactory = cmp.GetMachineFactory ();
363 Hashtable mapping = new Hashtable ();
364 machineFactory.Gap = psr.GetMapping (mapping);
365 machineFactory.Mapping = mapping;
366 machineFactory.NamesMapping = GetGroupNamesArray (machineFactory.GroupCount, machineFactory.Mapping);
368 return machineFactory;
371 protected Regex (SerializationInfo info, StreamingContext context) :
372 this (info.GetString ("pattern"),
373 (RegexOptions) info.GetValue ("options", typeof (RegexOptions)))
377 // public instance properties
379 public RegexOptions Options {
380 get { return roptions; }
383 public bool RightToLeft {
384 get { return (roptions & RegexOptions.RightToLeft) != 0; }
387 // public instance methods
389 public string [] GetGroupNames ()
391 string [] names = new string [1 + group_count];
392 Array.Copy (group_names, names, 1 + group_count);
396 public int [] GetGroupNumbers ()
398 int [] numbers = new int [1 + group_count];
399 Array.Copy (GroupNumbers, numbers, 1 + group_count);
403 public string GroupNameFromNumber (int i)
405 i = GetGroupIndex (i);
409 return group_names [i];
412 public int GroupNumberFromName (string name)
414 if (!mapping.Contains (name))
416 int i = (int) mapping [name];
418 i = Int32.Parse (name);
422 internal int GetGroupIndex (int number)
426 if (gap > group_count)
428 return Array.BinarySearch (GroupNumbers, gap, group_count - gap + 1, number);
431 int default_startat (string input)
433 return (RightToLeft && input != null) ? input.Length : 0;
438 public bool IsMatch (string input)
440 return IsMatch (input, default_startat (input));
443 public bool IsMatch (string input, int startat)
445 return Match (input, startat).Success;
448 public Match Match (string input)
450 return Match (input, default_startat (input));
453 public Match Match (string input, int startat)
456 throw new ArgumentNullException ("input");
457 if (startat < 0 || startat > input.Length)
458 throw new ArgumentOutOfRangeException ("startat");
459 return CreateMachine ().Scan (this, input, startat, input.Length);
462 public Match Match (string input, int beginning, int length)
465 throw new ArgumentNullException ("input");
466 if (beginning < 0 || beginning > input.Length)
467 throw new ArgumentOutOfRangeException ("beginning");
468 if (length < 0 || length > input.Length - beginning)
469 throw new ArgumentOutOfRangeException ("length");
470 return CreateMachine ().Scan (this, input, beginning, beginning + length);
473 public MatchCollection Matches (string input)
475 return Matches (input, default_startat (input));
478 public MatchCollection Matches (string input, int startat)
480 Match m = Match (input, startat);
481 return new MatchCollection (m);
486 public string Replace (string input, MatchEvaluator evaluator)
488 return Replace (input, evaluator, Int32.MaxValue, default_startat (input));
491 public string Replace (string input, MatchEvaluator evaluator, int count)
493 return Replace (input, evaluator, count, default_startat (input));
498 public Adapter (MatchEvaluator ev) { this.ev = ev; }
499 public void Evaluate (Match m, StringBuilder sb) { sb.Append (ev (m)); }
502 public string Replace (string input, MatchEvaluator evaluator, int count, int startat)
505 throw new ArgumentNullException ("input");
506 if (evaluator == null)
507 throw new ArgumentNullException ("evaluator");
509 throw new ArgumentOutOfRangeException ("count");
510 if (startat < 0 || startat > input.Length)
511 throw new ArgumentOutOfRangeException ("startat");
513 BaseMachine m = (BaseMachine)CreateMachine ();
516 return m.RTLReplace (this, input, evaluator, count, startat);
518 // NOTE: If this is a cause of a lot of allocations, we can convert it to
519 // use a ThreadStatic allocation mitigator
520 Adapter a = new Adapter (evaluator);
522 return m.LTRReplace (this, input, new BaseMachine.MatchAppendEvaluator (a.Evaluate),
526 public string Replace (string input, string replacement)
528 return Replace (input, replacement, Int32.MaxValue, default_startat (input));
531 public string Replace (string input, string replacement, int count)
533 return Replace (input, replacement, count, default_startat (input));
536 public string Replace (string input, string replacement, int count, int startat)
539 throw new ArgumentNullException ("input");
540 if (replacement == null)
541 throw new ArgumentNullException ("replacement");
543 throw new ArgumentOutOfRangeException ("count");
544 if (startat < 0 || startat > input.Length)
545 throw new ArgumentOutOfRangeException ("startat");
547 return CreateMachine ().Replace (this, input, replacement, count, startat);
552 public string [] Split (string input)
554 return Split (input, Int32.MaxValue, default_startat (input));
557 public string [] Split (string input, int count)
559 return Split (input, count, default_startat (input));
562 public string [] Split (string input, int count, int startat)
565 throw new ArgumentNullException ("input");
567 throw new ArgumentOutOfRangeException ("count");
568 if (startat < 0 || startat > input.Length)
569 throw new ArgumentOutOfRangeException ("startat");
571 return CreateMachine ().Split (this, input, count, startat);
574 // This method is called at the end of the constructor of compiled
575 // regular expression classes to do internal initialization.
576 protected void InitializeReferences ()
579 throw new NotSupportedException ("This operation is only allowed once per object.");
581 refsInitialized = true;
583 // Compile pattern that results in performance loss as existing
584 // CIL code is ignored but provides support for regular
585 // expressions compiled to assemblies.
589 protected bool UseOptionC ()
591 return ((roptions & RegexOptions.Compiled) != 0);
594 protected bool UseOptionR ()
596 return ((roptions & RegexOptions.RightToLeft) != 0);
601 public override string ToString ()
606 // ISerializable interface
607 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
609 info.AddValue ("pattern", this.ToString (), typeof (string));
610 info.AddValue ("options", this.Options, typeof (RegexOptions));
615 internal int GroupCount {
616 get { return group_count; }
625 private IMachine CreateMachine ()
627 return machineFactory.NewInstance ();
630 private static string [] GetGroupNamesArray (int groupCount, IDictionary mapping)
632 string [] group_names = new string [groupCount + 1];
633 IDictionaryEnumerator de = mapping.GetEnumerator ();
634 while (de.MoveNext ())
635 group_names [(int) de.Value] = (string) de.Key;
639 private int [] GroupNumbers {
641 if (group_numbers == null) {
642 group_numbers = new int [1 + group_count];
643 for (int i = 0; i < gap; ++i)
644 group_numbers [i] = i;
645 for (int i = gap; i <= group_count; ++i)
646 group_numbers [i] = Int32.Parse (group_names [i]);
647 return group_numbers;
649 return group_numbers;
653 private IMachineFactory machineFactory;
654 private IDictionary mapping;
655 private int group_count;
657 private bool refsInitialized;
658 private string [] group_names;
659 private int [] group_numbers;
663 protected internal string pattern;
664 protected internal RegexOptions roptions;
666 // MS undocumented members
669 internal System.Collections.Generic.Dictionary<string, int> capnames;
671 internal System.Collections.Generic.Dictionary<int, int> caps;
674 protected internal System.Collections.Hashtable capnames;
676 protected internal System.Collections.Hashtable caps;
679 protected internal RegexRunnerFactory factory;
682 protected internal int capsize;
684 protected internal string [] capslist;