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 {
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)
100 return Parser.Escape (str);
103 public static string Unescape (string str)
105 return Parser.Unescape (str);
108 public static bool IsMatch (string input, string pattern)
110 return IsMatch (input, pattern, RegexOptions.None);
113 public static bool IsMatch (string input, string pattern, RegexOptions options)
115 Regex re = new Regex (pattern, options);
116 return re.IsMatch (input);
119 public static Match Match (string input, string pattern)
121 return Regex.Match (input, pattern, RegexOptions.None);
124 public static Match Match (string input, string pattern, RegexOptions options)
126 Regex re = new Regex (pattern, options);
127 return re.Match (input);
130 public static MatchCollection Matches (string input, string pattern)
132 return Matches (input, pattern, RegexOptions.None);
135 public static MatchCollection Matches (string input, string pattern, RegexOptions options)
137 Regex re = new Regex (pattern, options);
138 return re.Matches (input);
141 public static string Replace (string input, string pattern, MatchEvaluator evaluator)
143 return Regex.Replace (input, pattern, evaluator, RegexOptions.None);
146 public static string Replace (string input, string pattern, MatchEvaluator evaluator,
147 RegexOptions options)
149 Regex re = new Regex (pattern, options);
150 return re.Replace (input, evaluator);
153 public static string Replace (string input, string pattern, string replacement)
155 return Regex.Replace (input, pattern, replacement, RegexOptions.None);
158 public static string Replace (string input, string pattern, string replacement,
159 RegexOptions options)
161 Regex re = new Regex (pattern, options);
162 return re.Replace (input, replacement);
165 public static string [] Split (string input, string pattern)
167 return Regex.Split (input, pattern, RegexOptions.None);
170 public static string [] Split (string input, string pattern, RegexOptions options)
172 Regex re = new Regex (pattern, options);
173 return re.Split (input);
177 static FactoryCache cache = new FactoryCache (15);
178 public static int CacheSize {
179 get { return cache.Capacity; }
182 throw new ArgumentOutOfRangeException ("CacheSize");
184 cache.Capacity = value;
188 static FactoryCache cache = new FactoryCache (200);
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)
208 this.pattern = pattern;
209 this.roptions = options;
215 this.machineFactory = cache.Lookup (this.pattern, this.roptions);
217 if (this.machineFactory == null) {
220 this.group_count = this.machineFactory.GroupCount;
221 this.mapping = this.machineFactory.Mapping;
222 this.group_names = this.machineFactory.NamesMapping;
227 private void InitNewRegex ()
229 this.machineFactory = CreateMachineFactory (this.pattern, this.roptions);
230 cache.Add (this.pattern, this.roptions, this.machineFactory);
231 this.group_count = machineFactory.GroupCount;
232 this.mapping = machineFactory.Mapping;
233 this.group_names = this.machineFactory.NamesMapping;
237 // The new rx engine has blocking bugs like
238 // https://bugzilla.novell.com/show_bug.cgi?id=470827
239 static readonly bool old_rx =
240 Environment.GetEnvironmentVariable ("MONO_NEW_RX") == null;
243 private static IMachineFactory CreateMachineFactory (string pattern, RegexOptions options)
245 Parser psr = new Parser ();
246 RegularExpression re = psr.ParseRegularExpression (pattern, options);
249 ICompiler cmp = new PatternCompiler ();
253 if ((options & RegexOptions.Compiled) != 0)
254 cmp = new CILCompiler ();
256 cmp = new RxCompiler ();
258 cmp = new PatternCompiler ();
262 re.Compile (cmp, (options & RegexOptions.RightToLeft) != 0);
264 IMachineFactory machineFactory = cmp.GetMachineFactory ();
265 Hashtable mapping = new Hashtable ();
266 machineFactory.Gap = psr.GetMapping (mapping);
267 machineFactory.Mapping = mapping;
268 machineFactory.NamesMapping = GetGroupNamesArray (machineFactory.GroupCount, machineFactory.Mapping);
270 return machineFactory;
278 Regex (SerializationInfo info, StreamingContext context) :
279 this (info.GetString ("pattern"),
280 (RegexOptions) info.GetValue ("options", typeof (RegexOptions)))
284 #if ONLY_1_1 && !TARGET_JVM
285 // fixes public API signature
290 // public instance properties
292 public RegexOptions Options {
293 get { return roptions; }
296 public bool RightToLeft {
297 get { return (roptions & RegexOptions.RightToLeft) != 0; }
300 // public instance methods
302 public string [] GetGroupNames ()
304 string [] names = new string [1 + group_count];
305 Array.Copy (group_names, names, 1 + group_count);
309 public int[] GetGroupNumbers ()
311 int[] numbers = new int [1 + group_count];
312 for (int i = 0; i <= group_count; ++i)
314 // FIXME: needs to handle arbitrarily numbered groups '(?<43>abc)'
318 public string GroupNameFromNumber (int i)
320 i = GetGroupIndex (i);
324 return group_names [i];
327 public int GroupNumberFromName (string name)
329 if (mapping.Contains (name))
330 return (int) mapping [name];
335 internal int GetGroupIndex (int number)
337 int gap = group_count + 1;
340 // FIXME: handle arbitrarily numbered groups here
346 public bool IsMatch (string input)
348 return IsMatch (input, RightToLeft ? input.Length : 0);
351 public bool IsMatch (string input, int startat)
353 return Match (input, startat).Success;
356 public Match Match (string input)
358 return Match (input, RightToLeft ? input.Length : 0);
361 public Match Match (string input, int startat)
363 return CreateMachine ().Scan (this, input, startat, input.Length);
366 public Match Match (string input, int startat, int length)
368 return CreateMachine ().Scan (this, input, startat, startat + length);
371 public MatchCollection Matches (string input)
373 return Matches (input, RightToLeft ? input.Length : 0);
376 public MatchCollection Matches (string input, int startat)
378 Match m = Match (input, startat);
379 return new MatchCollection (m);
384 public string Replace (string input, MatchEvaluator evaluator)
386 return Replace (input, evaluator, Int32.MaxValue, RightToLeft ? input.Length : 0);
389 public string Replace (string input, MatchEvaluator evaluator, int count)
391 return Replace (input, evaluator, count, RightToLeft ? input.Length : 0);
396 public Adapter (MatchEvaluator ev) { this.ev = ev; }
397 public void Evaluate (Match m, StringBuilder sb) { sb.Append (ev (m)); }
400 public string Replace (string input, MatchEvaluator evaluator, int count, int startat)
403 throw new ArgumentNullException ("null");
404 if (evaluator == null)
405 throw new ArgumentNullException ("evaluator");
407 BaseMachine m = (BaseMachine)CreateMachine ();
410 return m.RTLReplace (this, input, evaluator, count, startat);
412 // NOTE: If this is a cause of a lot of allocations, we can convert it to
413 // use a ThreadStatic allocation mitigator
414 Adapter a = new Adapter (evaluator);
416 return m.LTRReplace (this, input, new BaseMachine.MatchAppendEvaluator (a.Evaluate),
420 public string Replace (string input, string replacement)
422 return Replace (input, replacement, Int32.MaxValue, RightToLeft ? input.Length : 0);
425 public string Replace (string input, string replacement, int count)
427 return Replace (input, replacement, count, RightToLeft ? input.Length : 0);
430 public string Replace (string input, string replacement, int count, int startat)
432 return CreateMachine ().Replace (this, input, replacement, count, startat);
437 public string [] Split (string input)
439 return Split (input, Int32.MaxValue, RightToLeft ? input.Length : 0);
442 public string [] Split (string input, int count)
444 return Split (input, count, RightToLeft ? input.Length : 0);
447 public string [] Split (string input, int count, int startat)
449 return CreateMachine ().Split (this, input, count, startat);
452 // This method is called at the end of the constructor of compiled
453 // regular expression classes to do internal initialization.
454 protected void InitializeReferences ()
457 throw new NotSupportedException ("This operation is only allowed once per object.");
459 refsInitialized = true;
461 // Compile pattern that results in performance loss as existing
462 // CIL code is ignored but provides support for regular
463 // expressions compiled to assemblies.
467 protected bool UseOptionC ()
469 return ((roptions & RegexOptions.Compiled) != 0);
472 protected bool UseOptionR ()
474 return ((roptions & RegexOptions.RightToLeft) != 0);
479 public override string ToString ()
484 // ISerializable interface
485 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
487 info.AddValue ("pattern", this.ToString (), typeof (string));
488 info.AddValue ("options", this.Options, typeof (RegexOptions));
493 internal int GroupCount {
494 get { return group_count; }
499 private IMachine CreateMachine ()
501 return machineFactory.NewInstance ();
504 private static string [] GetGroupNamesArray (int groupCount, IDictionary mapping)
506 string [] group_names = new string [groupCount + 1];
507 IDictionaryEnumerator de = mapping.GetEnumerator ();
508 while (de.MoveNext ())
509 group_names [(int) de.Value] = (string) de.Key;
513 private IMachineFactory machineFactory;
514 private IDictionary mapping;
515 private int group_count;
516 private bool refsInitialized;
517 private string [] group_names;
522 protected internal string pattern;
523 protected internal RegexOptions roptions;
525 // MS undocumented members
528 internal System.Collections.Generic.Dictionary<string, int> capnames;
530 internal System.Collections.Generic.Dictionary<int, int> caps;
533 protected internal System.Collections.Hashtable capnames;
535 protected internal System.Collections.Hashtable caps;
538 protected internal RegexRunnerFactory factory;
541 protected internal int capsize;
543 protected internal string [] capslist;