context: /****************************************************************************
  **
  ** 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;
  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 )
      ///
      ////////////////////////////////////////////////////////////////////////////////////////////////
      /// <summary>
      /// Represents a complex class defined by a combination of elementary character classes.
      /// </summary>
      ///
      public partial class CharClassSet : CharClass, IEnumerable<IEnumerable<Element>>
      {
          public IEnumerable<Element> Positives { get; set; }
          public IEnumerable<Element> 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<RegExpr> OnRender(RegExpr defaultTokenWs, RegExpr parent,
              StringBuilder pattern, ref RenderMode mode)
          {
              base.OnRender(defaultTokenWs, parent, pattern, ref mode);
  
              if (!HasPositive && !HasNegative)
                  return null;
  
              if (!IsSubSet) {
                  if (HasPositive)
                      pattern.Append("[");
                  else
                      pattern.Append("[^");
              }
  
              IEnumerable<RegExpr> 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)
          {
              base.OnRenderNext(defaultTokenWs, parent, pattern, ref mode);
              if (!IsSubSet && HasPositive && HasNegative)
                  pattern.Append("-[");
          }
  
          protected override void OnRenderEnd(RegExpr defaultTokenWs, RegExpr parent,
              StringBuilder pattern, ref RenderMode mode)
          {
              base.OnRenderEnd(defaultTokenWs, parent, pattern, ref mode);
              if (!IsSubSet) {
                  if (HasPositive && HasNegative)
                      pattern.Append("]]");
                  else
                      pattern.Append("]");
              }
          }
  
          public const bool Invert = true;
          public const bool Positive = true;
  
          public CharClassSet(
              IEnumerable<Element> positives = null,
              IEnumerable<Element> negatives = null)
          {
              Positives = positives != null ? positives : Empty<Element>();
              Negatives = negatives != null ? negatives : Empty<Element>();
          }
  
          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<IEnumerable<Element>> 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; private set; }
  
                  public List<Expr> Factors { get; private set; }
                  public Expr(Op op, List<Expr> factors) { Operator = op; Factors = factors; }
                  public Expr(Op op, params Expr[] factors) : this(op, factors.ToList()) { }
  
                  public CharClass Term { get; private set; }
                  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 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<StackFrame>();
                      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<Expr>();
                              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() },
  
gt:                                         { context.SubSets.Last(), Invert }