/**************************************************************************** ** ** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt VS Tools. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; namespace QtVsTools.SyntaxAnalysis { using static CharClassSet; using static CharClass.CharSetExprBuilder; //////////////////////////////////////////////////////////////////////////////////////////////// /// /// CharClassSet ( -> CharClass -> RegExpr ) /// //////////////////////////////////////////////////////////////////////////////////////////////// /// /// Represents a complex class defined by a combination of elementary character classes. /// /// public partial class CharClassSet : CharClass, IEnumerable> { private IEnumerable Positives { get; set; } private IEnumerable Negatives { get; set; } bool IsSubSet { get; set; } bool HasPositive { get { return Positives != null && Positives.Any(); } } bool HasNegative { get { return Negatives != null && Negatives.Any(); } } protected override IEnumerable OnRender(RegExpr defaultTokenWs, RegExpr parent, StringBuilder pattern, ref RenderMode mode, Stack tokenStack) { base.OnRender(defaultTokenWs, parent, pattern, ref mode, tokenStack); if (!HasPositive && !HasNegative) return null; if (!IsSubSet) { if (HasPositive) pattern.Append("["); else pattern.Append("[^"); } IEnumerable children = null; if (HasPositive && HasNegative) { children = Items( new CharClassSet(positives: Positives) { IsSubSet = true }, new CharClassSet(negatives: Negatives) { IsSubSet = true }); } else { if (HasPositive) children = Positives; else if (HasNegative) children = Negatives; } return children; } protected override void OnRenderNext(RegExpr defaultTokenWs, RegExpr parent, StringBuilder pattern, ref RenderMode mode, Stack tokenStack) { base.OnRenderNext(defaultTokenWs, parent, pattern, ref mode, tokenStack); if (!IsSubSet && HasPositive && HasNegative) pattern.Append("-["); } protected override void OnRenderEnd(RegExpr defaultTokenWs, RegExpr parent, StringBuilder pattern, ref RenderMode mode, Stack tokenStack) { base.OnRenderEnd(defaultTokenWs, parent, pattern, ref mode, tokenStack); if (!IsSubSet) { if (HasPositive && HasNegative) pattern.Append("]]"); else pattern.Append("]"); } } public const bool Invert = true; public const bool Positive = true; public CharClassSet( IEnumerable positives = null, IEnumerable negatives = null) { Positives = positives != null ? positives : Empty(); Negatives = negatives != null ? negatives : Empty(); } public CharClassSet(Element element, bool negative = false) : this() { if (negative) Negatives = Items(element); else Positives = Items(element); } public CharClassSet(CharClassSet set, bool invert = false) : this() { Add(set, invert); } public void Add(Element element, bool negative = false) { if (negative) Negatives = Negatives.Concat(Items(element)); else Positives = Positives.Concat(Items(element)); } public void Add(CharClassSet set, bool invert = false) { Positives = Positives.Concat(!invert ? set.Positives : set.Negatives); Negatives = Negatives.Concat(!invert ? set.Negatives : set.Positives); } public IEnumerator> GetEnumerator() { return (new[] { Positives, Negatives }).AsEnumerable().GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public abstract class Element : CharClass { public static CharClassSet operator ~(Element x) { return new CharClassSet(x, negative: true); } public static PositiveSet operator +(Element x, Element y) { return new PositiveSet(Op.Plus, x, y); } public static PositiveSet operator +(Element x, PositiveSet y) { return new PositiveSet(Op.Plus, x, y); } public static PositiveSet operator +(PositiveSet x, Element y) { return new PositiveSet(Op.Plus, x, y); } public static Expr operator -(Element x, Element y) { return new Expr(Op.Minus, x, y); } public static Expr operator -(Element x, PositiveSet y) { return new Expr(Op.Minus, x, y); } public static Expr operator -(PositiveSet x, Element y) { return new Expr(Op.Minus, x, y); } } public static CharClassSet operator ~(CharClassSet x) { return new CharClassSet(x, invert: true); } public static Expr operator +(Element x, CharClassSet y) { return new Expr(Op.Plus, x, y); } public static Expr operator +(CharClassSet x, Element y) { return new Expr(Op.Plus, x, y); } public static Expr operator +(PositiveSet x, CharClassSet y) { return new Expr(Op.Plus, x, y); } public static Expr operator +(CharClassSet x, PositiveSet y) { return new Expr(Op.Plus, x, y); } public static Expr operator +(CharClassSet x, CharClassSet y) { return new Expr(Op.Plus, x, y); } public static Expr operator -(CharClassSet x, Element y) { return new Expr(Op.Minus, x, y); } public static Expr operator -(CharClassSet x, PositiveSet y) { return new Expr(Op.Minus, x, y); } } public abstract partial class CharClass : RegExpr { public partial class CharSetExprBuilder { public enum Op { Term, Tilde, Plus, Minus } public class Expr { public Op Operator { get; } public List Factors { get; } public Expr(Op op, List factors) { Operator = op; Factors = factors; } public Expr(Op op, params Expr[] factors) : this(op, factors.ToList()) { } public CharClass Term { get; } public Expr(CharClass c) { Operator = Op.Term; Term = c; } public static implicit operator Expr(CharClass c) { return new Expr(c); } public static implicit operator Expr(string s) { return Char[s]; } public static implicit operator Expr(char c) { return Char[c]; } public static Expr operator ~(Expr x) { return new Expr(Op.Tilde, x); } } public class PositiveSet : Expr { public PositiveSet(Op op, params Expr[] factors) : base(op, factors) { } public static PositiveSet operator +(PositiveSet x, PositiveSet y) { return new PositiveSet(Op.Plus, x, y); } public static Expr operator -(PositiveSet x, PositiveSet y) { return new Expr(Op.Minus, x, y); } } public CharClassSet this[params Expr[] exprs] { get { return this[new PositiveSet(Op.Plus, exprs)]; } } public CharClassSet this[Expr expr] { get { var stack = new Stack(); stack.Push(expr); CharClassSet classSet = null; while (stack.Any()) { var context = stack.Pop(); expr = context.Expr; if (context == null || expr == null) { continue; } else if (context.Children == null) { context.Children = new Queue(); if (expr.Factors != null && expr.Factors.Any()) expr.Factors.ForEach(x => context.Children.Enqueue(x)); stack.Push(context); continue; } else if (context.Children.Any()) { expr = context.Children.Dequeue(); stack.Push(context); stack.Push(expr); continue; } classSet = null; if (expr.Operator == Op.Term) { if (expr.Term is CharClassSet) classSet = expr.Term as CharClassSet; else classSet = new CharClassSet(expr.Term as Element); } else if (context.SubSets != null && context.SubSets.Any()) { switch (expr.Operator) { case Op.Tilde: classSet = new CharClassSet { { context.SubSets.First(), Invert } }; break; case Op.Plus: classSet = new CharClassSet { { context.SubSets.First() }, { context.SubSets.Last() } }; break; case Op.Minus: classSet = new CharClassSet { { context.SubSets.First() }, { context.SubSets.Last(), Invert } }; break; } } var parentContext = stack.Any() ? stack.Peek() : null; if (classSet != null && parentContext != null) parentContext.SubSets.Add(classSet); } if (classSet == null) throw new CharClassEvalException(); return classSet; } } public CharClassSet this[IEnumerable chars] { get { return this[chars.Select(c => (Expr)c).ToArray()]; } } class StackFrame { public Expr Expr { get; set; } public Queue Children { get; set; } public List SubSets { get; } public StackFrame() { Expr = null; Children = null; SubSets = new List(); } public static implicit operator StackFrame(Expr e) { return new StackFrame { Expr = e }; } } } public partial class CharSetRawExprBuilder { public CharClassLiteral this[string s] { get { return CharRawLiteral(s); } } } public class CharClassEvalException : RegExprException { public CharClassEvalException(string message = null) : base(message) { } } } }