// Copyright © 2007 John M Rusk (http://dotnet.agilekiwi.com) // // You may use this source code ("The Software") in any manner you wish, // subject to the following conditions: // // (a) The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // (b) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. using System; using System.Text; namespace AgileKiwi.RegularExpressions { // A more readable way to create regular expressions. // In this version, we only support the features required to implement this example: http://dotnet.agilekiwi.com/blog/2006/10/shorthand-interfaces.html // Full regex support is not present in this version // note that most methods are static, because we intend to use them // in "base" calls from constructors, // and instance methods cannot be used in that way public class SmartRegex { public SmartRegex(params Element[] elements) { _elements = elements; } readonly Element[] _elements; public override string ToString() { return ElementsToString(_elements); } internal static string ElementsToString(Element[] elements) { StringBuilder result = new StringBuilder(); foreach (Element element in elements) result.Append(element.ToString()); return result.ToString(); } static public SimpleElement Literal(string s) { return new SimpleElement(s); } static public SimpleElement Anything { get { return new SimpleElement("."); } } static public SimpleElement Digit { get { return new SimpleElement(@"\d"); } } static public SimpleElement Space { get { return new SimpleElement(@"\s"); } } static public LazyInt Lazy(int value) { { return new LazyInt(value); } } static public GroupElement Group(params Element[] elements) { return new GroupElement(elements); } } #region Helper classes public abstract class Element { public static implicit operator Element(string literal) { return new SimpleElement(literal); } } /// /// Element that can be followed by a quantifier /// public abstract class QuantifiableElement: Element { public static Element operator >= (QuantifiableElement lhs, int rhs) { return new QuantifiedElement(lhs, rhs, null, MatchType.Greedy); } public static Element operator <= (QuantifiableElement lhs, int rhs) { return new QuantifiedElement(lhs, 0, rhs, MatchType.Greedy); } public static Element operator >=(QuantifiableElement lhs, LazyInt rhs) { return new QuantifiedElement(lhs, rhs.Value, null, MatchType.Lazy); } public static Element operator <=(QuantifiableElement lhs, LazyInt rhs) { return new QuantifiedElement(lhs, 0, rhs.Value, MatchType.Lazy); } } public class SimpleElement : QuantifiableElement { public SimpleElement(string s) { _s = s; } string _s; public override string ToString() { return _s; } } public class QuantifiedElement : Element { public QuantifiedElement(QuantifiableElement inner, int min, int? max, MatchType matchType) { _inner = inner; _min = min; _max = max; _matchType = matchType; } QuantifiableElement _inner; int _min; int? _max; MatchType _matchType; public override string ToString() { string maxString = _max.HasValue ? _max.ToString() : string.Empty; string quantifierString = "{" + _min + "," + maxString + "}"; //handle some special cases if (quantifierString == "{0,}") quantifierString = "*"; else if (quantifierString == "{1,}") quantifierString = "+"; else if (quantifierString == "{0,1}") quantifierString = "?"; // doesn't really make sense to make this lazy, but we cannot stop the user from doing so in our current design if (_matchType == MatchType.Lazy) quantifierString += "?"; // else, // do nothing, since greedy is the default return _inner.ToString() + quantifierString; } } public enum MatchType { Greedy, Lazy } public class LazyInt { public LazyInt(int value) { Value = value; } public readonly int Value; } public class GroupElement: QuantifiableElement { public GroupElement(params Element[] elements) { _elements = elements; } Element[] _elements; string _name; public override string ToString() { string nameString = _name == null ? string.Empty : "?<" + _name + ">"; return "(" + nameString + SmartRegex.ElementsToString(_elements) + ")"; } public GroupElement Named(string name) { _name = name; return this; } } #endregion }