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;
34 using System.Reflection.Emit;
35 using System.Runtime.Serialization;
37 using RegularExpression = System.Text.RegularExpressions.Syntax.RegularExpression;
38 using Parser = System.Text.RegularExpressions.Syntax.Parser;
40 using System.Diagnostics;
43 namespace System.Text.RegularExpressions {
46 public partial class Regex : ISerializable {
48 #if !TARGET_JVM && !FULL_AOT_RUNTIME
50 public static void CompileToAssembly (RegexCompilationInfo [] regexes, AssemblyName aname)
52 Regex.CompileToAssembly(regexes, aname, new CustomAttributeBuilder [] {}, null);
56 public static void CompileToAssembly (RegexCompilationInfo [] regexes, AssemblyName aname,
57 CustomAttributeBuilder [] attribs)
59 Regex.CompileToAssembly(regexes, aname, attribs, null);
63 public static void CompileToAssembly (RegexCompilationInfo [] regexes, AssemblyName aname,
64 CustomAttributeBuilder [] attribs, string resourceFile)
66 throw new NotImplementedException ();
67 // TODO : Make use of attribs and resourceFile parameters
69 AssemblyBuilder asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly (aname, AssemblyBuilderAccess.RunAndSave);
70 ModuleBuilder modBuilder = asmBuilder.DefineDynamicModule("InnerRegexModule",aname.Name);
71 Parser psr = new Parser ();
73 System.Console.WriteLine("CompileToAssembly");
75 for(int i=0; i < regexes.Length; i++)
77 System.Console.WriteLine("Compiling expression :" + regexes[i].Pattern);
78 RegularExpression re = psr.ParseRegularExpression (regexes[i].Pattern, regexes[i].Options);
82 CILCompiler cmp = new CILCompiler (modBuilder, i);
83 bool reverse = (regexes[i].Options & RegexOptions.RightToLeft) !=0;
84 re.Compile (cmp, reverse);
90 // Define a runtime class with specified name and attributes.
91 TypeBuilder builder = modBuilder.DefineType("ITest");
93 asmBuilder.Save(aname.Name);
98 public static string Escape (string str)
101 throw new ArgumentNullException ("str");
102 return Parser.Escape (str);
105 public static string Unescape (string str)
108 throw new ArgumentNullException ("str");
109 return Parser.Unescape (str);
112 public static bool IsMatch (string input, string pattern)
114 return IsMatch (input, pattern, RegexOptions.None);
117 public static bool IsMatch (string input, string pattern, RegexOptions options)
119 Regex re = new Regex (pattern, options);
120 return re.IsMatch (input);
123 public static Match Match (string input, string pattern)
125 return Regex.Match (input, pattern, RegexOptions.None);
128 public static Match Match (string input, string pattern, RegexOptions options)
130 Regex re = new Regex (pattern, options);
131 return re.Match (input);
134 public static MatchCollection Matches (string input, string pattern)
136 return Matches (input, pattern, RegexOptions.None);
139 public static MatchCollection Matches (string input, string pattern, RegexOptions options)
141 Regex re = new Regex (pattern, options);
142 return re.Matches (input);
145 public static string Replace (string input, string pattern, MatchEvaluator evaluator)
147 return Regex.Replace (input, pattern, evaluator, RegexOptions.None);
150 public static string Replace (string input, string pattern, MatchEvaluator evaluator,
151 RegexOptions options)
153 Regex re = new Regex (pattern, options);
154 return re.Replace (input, evaluator);
157 public static string Replace (string input, string pattern, string replacement)
159 return Regex.Replace (input, pattern, replacement, RegexOptions.None);
162 public static string Replace (string input, string pattern, string replacement,
163 RegexOptions options)
165 Regex re = new Regex (pattern, options);
166 return re.Replace (input, replacement);
169 public static string [] Split (string input, string pattern)
171 return Regex.Split (input, pattern, RegexOptions.None);
174 public static string [] Split (string input, string pattern, RegexOptions options)
176 Regex re = new Regex (pattern, options);
177 return re.Split (input);
180 static FactoryCache cache = new FactoryCache (15);
181 public static int CacheSize {
182 get { return cache.Capacity; }
185 throw new ArgumentOutOfRangeException ("CacheSize");
187 cache.Capacity = value;
196 // This constructor is used by compiled regular expressions that are
197 // classes derived from Regex class. No initialization required.
202 public Regex (string pattern) : this (pattern, RegexOptions.None)
206 public Regex (string pattern, RegexOptions options)
209 throw new ArgumentNullException ("pattern");
210 validate_options (options);
211 this.pattern = pattern;
212 this.roptions = options;
217 [MonoTODO ("Timeouts are ignored.")]
218 public Regex (string pattern, RegexOptions options, TimeSpan matchTimeout)
219 : this (pattern, options)
221 MatchTimeout = matchTimeout;
224 [MonoTODO ("Timeouts are ignored.")]
225 public TimeSpan MatchTimeout {
230 [MonoTODO ("Timeouts are ignored.")]
231 public static bool IsMatch (
232 string input, string pattern, RegexOptions options, TimeSpan matchTimeout)
234 return IsMatch (input, pattern, options);
237 [MonoTODO ("Timeouts are ignored.")]
238 public static Match Match (
239 string input, string pattern, RegexOptions options, TimeSpan matchTimeout)
241 return Match (input, pattern, options);
244 [MonoTODO ("Timeouts are ignored.")]
245 public static MatchCollection Matches (
246 string input, string pattern, RegexOptions options, TimeSpan matchTimeout)
248 return Matches (input, pattern, options, matchTimeout);
251 [MonoTODO ("Timeouts are ignored.")]
252 public static string Replace (
253 string input, string pattern, string replacement, RegexOptions options,
254 TimeSpan matchTimeout)
256 return Replace (input, pattern, replacement, options);
259 [MonoTODO ("Timeouts are ignored.")]
260 public static string Replace (
261 string input, string pattern, MatchEvaluator evaluator, RegexOptions options,
262 TimeSpan matchTimeout)
264 return Replace (input, pattern, evaluator, options);
267 [MonoTODO ("Timeouts are ignored.")]
268 public static string[] Split (
269 string input, string pattern, RegexOptions options, TimeSpan matchTimeout)
271 return Split (input, pattern, options);
275 static void validate_options (RegexOptions options)
277 const RegexOptions allopts =
279 RegexOptions.IgnoreCase |
280 RegexOptions.Multiline |
281 RegexOptions.ExplicitCapture |
282 #if MOBILE || !NET_2_1
283 RegexOptions.Compiled |
285 RegexOptions.Singleline |
286 RegexOptions.IgnorePatternWhitespace |
287 RegexOptions.RightToLeft |
288 RegexOptions.ECMAScript |
289 RegexOptions.CultureInvariant;
291 const RegexOptions ecmaopts =
292 RegexOptions.IgnoreCase |
293 RegexOptions.Multiline |
294 #if MOBILE || !NET_2_1
295 RegexOptions.Compiled |
297 RegexOptions.ECMAScript;
299 if ((options & ~allopts) != 0)
300 throw new ArgumentOutOfRangeException ("options");
301 if ((options & RegexOptions.ECMAScript) != 0 && (options & ~ecmaopts) != 0)
302 throw new ArgumentOutOfRangeException ("options");
308 this.machineFactory = cache.Lookup (this.pattern, this.roptions);
310 if (this.machineFactory == null) {
313 this.group_count = this.machineFactory.GroupCount;
314 this.gap = this.machineFactory.Gap;
315 this.mapping = this.machineFactory.Mapping;
316 this.group_names = this.machineFactory.NamesMapping;
321 private void InitNewRegex ()
323 this.machineFactory = CreateMachineFactory (this.pattern, this.roptions);
324 cache.Add (this.pattern, this.roptions, this.machineFactory);
325 this.group_count = machineFactory.GroupCount;
326 this.gap = this.machineFactory.Gap;
327 this.mapping = machineFactory.Mapping;
328 this.group_names = this.machineFactory.NamesMapping;
332 // The new rx engine seems to be working now, but
333 // potential problems are being tracked down here:
334 // https://bugzilla.novell.com/show_bug.cgi?id=470827
335 static readonly bool old_rx =
336 Environment.GetEnvironmentVariable ("MONO_NEW_RX") == null;
339 private static IMachineFactory CreateMachineFactory (string pattern, RegexOptions options)
341 Parser psr = new Parser ();
342 RegularExpression re = psr.ParseRegularExpression (pattern, options);
345 ICompiler cmp = new PatternCompiler ();
349 if ((options & RegexOptions.Compiled) != 0)
350 cmp = new CILCompiler ();
352 cmp = new RxCompiler ();
354 cmp = new PatternCompiler ();
358 re.Compile (cmp, (options & RegexOptions.RightToLeft) != 0);
360 IMachineFactory machineFactory = cmp.GetMachineFactory ();
361 Hashtable mapping = new Hashtable ();
362 machineFactory.Gap = psr.GetMapping (mapping);
363 machineFactory.Mapping = mapping;
364 machineFactory.NamesMapping = GetGroupNamesArray (machineFactory.GroupCount, machineFactory.Mapping);
366 return machineFactory;
369 protected Regex (SerializationInfo info, StreamingContext context) :
370 this (info.GetString ("pattern"),
371 (RegexOptions) info.GetValue ("options", typeof (RegexOptions)))
375 // public instance properties
377 public RegexOptions Options {
378 get { return roptions; }
381 public bool RightToLeft {
382 get { return (roptions & RegexOptions.RightToLeft) != 0; }
385 // public instance methods
387 public string [] GetGroupNames ()
389 string [] names = new string [1 + group_count];
390 Array.Copy (group_names, names, 1 + group_count);
394 public int [] GetGroupNumbers ()
396 int [] numbers = new int [1 + group_count];
397 Array.Copy (GroupNumbers, numbers, 1 + group_count);
401 public string GroupNameFromNumber (int i)
403 i = GetGroupIndex (i);
407 return group_names [i];
410 public int GroupNumberFromName (string name)
412 if (!mapping.Contains (name))
414 int i = (int) mapping [name];
416 i = Int32.Parse (name);
420 internal int GetGroupIndex (int number)
424 if (gap > group_count)
426 return Array.BinarySearch (GroupNumbers, gap, group_count - gap + 1, number);
429 int default_startat (string input)
431 return (RightToLeft && input != null) ? input.Length : 0;
436 public bool IsMatch (string input)
438 return IsMatch (input, default_startat (input));
441 public bool IsMatch (string input, int startat)
443 return Match (input, startat).Success;
446 public Match Match (string input)
448 return Match (input, default_startat (input));
451 public Match Match (string input, int startat)
454 throw new ArgumentNullException ("input");
455 if (startat < 0 || startat > input.Length)
456 throw new ArgumentOutOfRangeException ("startat");
457 return CreateMachine ().Scan (this, input, startat, input.Length);
460 public Match Match (string input, int beginning, int length)
463 throw new ArgumentNullException ("input");
464 if (beginning < 0 || beginning > input.Length)
465 throw new ArgumentOutOfRangeException ("beginning");
466 if (length < 0 || length > input.Length - beginning)
467 throw new ArgumentOutOfRangeException ("length");
468 return CreateMachine ().Scan (this, input, beginning, beginning + length);
471 public MatchCollection Matches (string input)
473 return Matches (input, default_startat (input));
476 public MatchCollection Matches (string input, int startat)
478 Match m = Match (input, startat);
479 return new MatchCollection (m);
484 public string Replace (string input, MatchEvaluator evaluator)
486 return Replace (input, evaluator, Int32.MaxValue, default_startat (input));
489 public string Replace (string input, MatchEvaluator evaluator, int count)
491 return Replace (input, evaluator, count, default_startat (input));
496 public Adapter (MatchEvaluator ev) { this.ev = ev; }
497 public void Evaluate (Match m, StringBuilder sb) { sb.Append (ev (m)); }
500 public string Replace (string input, MatchEvaluator evaluator, int count, int startat)
503 throw new ArgumentNullException ("input");
504 if (evaluator == null)
505 throw new ArgumentNullException ("evaluator");
507 throw new ArgumentOutOfRangeException ("count");
508 if (startat < 0 || startat > input.Length)
509 throw new ArgumentOutOfRangeException ("startat");
511 BaseMachine m = (BaseMachine)CreateMachine ();
514 return m.RTLReplace (this, input, evaluator, count, startat);
516 // NOTE: If this is a cause of a lot of allocations, we can convert it to
517 // use a ThreadStatic allocation mitigator
518 Adapter a = new Adapter (evaluator);
520 return m.LTRReplace (this, input, new BaseMachine.MatchAppendEvaluator (a.Evaluate),
524 public string Replace (string input, string replacement)
526 return Replace (input, replacement, Int32.MaxValue, default_startat (input));
529 public string Replace (string input, string replacement, int count)
531 return Replace (input, replacement, count, default_startat (input));
534 public string Replace (string input, string replacement, int count, int startat)
537 throw new ArgumentNullException ("input");
538 if (replacement == null)
539 throw new ArgumentNullException ("replacement");
541 throw new ArgumentOutOfRangeException ("count");
542 if (startat < 0 || startat > input.Length)
543 throw new ArgumentOutOfRangeException ("startat");
545 return CreateMachine ().Replace (this, input, replacement, count, startat);
550 public string [] Split (string input)
552 return Split (input, Int32.MaxValue, default_startat (input));
555 public string [] Split (string input, int count)
557 return Split (input, count, default_startat (input));
560 public string [] Split (string input, int count, int startat)
563 throw new ArgumentNullException ("input");
565 throw new ArgumentOutOfRangeException ("count");
566 if (startat < 0 || startat > input.Length)
567 throw new ArgumentOutOfRangeException ("startat");
569 return CreateMachine ().Split (this, input, count, startat);
572 // This method is called at the end of the constructor of compiled
573 // regular expression classes to do internal initialization.
574 protected void InitializeReferences ()
577 throw new NotSupportedException ("This operation is only allowed once per object.");
579 refsInitialized = true;
581 // Compile pattern that results in performance loss as existing
582 // CIL code is ignored but provides support for regular
583 // expressions compiled to assemblies.
587 protected bool UseOptionC ()
589 return ((roptions & RegexOptions.Compiled) != 0);
592 protected bool UseOptionR ()
594 return ((roptions & RegexOptions.RightToLeft) != 0);
599 public override string ToString ()
604 // ISerializable interface
605 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
607 info.AddValue ("pattern", this.ToString (), typeof (string));
608 info.AddValue ("options", this.Options, typeof (RegexOptions));
613 internal int GroupCount {
614 get { return group_count; }
623 private IMachine CreateMachine ()
625 return machineFactory.NewInstance ();
628 private static string [] GetGroupNamesArray (int groupCount, IDictionary mapping)
630 string [] group_names = new string [groupCount + 1];
631 IDictionaryEnumerator de = mapping.GetEnumerator ();
632 while (de.MoveNext ())
633 group_names [(int) de.Value] = (string) de.Key;
637 private int [] GroupNumbers {
639 if (group_numbers == null) {
640 group_numbers = new int [1 + group_count];
641 for (int i = 0; i < gap; ++i)
642 group_numbers [i] = i;
643 for (int i = gap; i <= group_count; ++i)
644 group_numbers [i] = Int32.Parse (group_names [i]);
645 return group_numbers;
647 return group_numbers;
651 private IMachineFactory machineFactory;
652 private IDictionary mapping;
653 private int group_count;
655 private bool refsInitialized;
656 private string [] group_names;
657 private int [] group_numbers;
661 protected internal string pattern;
662 protected internal RegexOptions roptions;
664 // MS undocumented members
667 internal System.Collections.Generic.Dictionary<string, int> capnames;
669 internal System.Collections.Generic.Dictionary<int, int> caps;
672 protected internal System.Collections.Hashtable capnames;
674 protected internal System.Collections.Hashtable caps;
677 protected internal RegexRunnerFactory factory;
680 protected internal int capsize;
682 protected internal string [] capslist;