/****************************************************************************
**
** 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 )
///
////////////////////////////////////////////////////////////////////////////////////////////////
///
/// Represents a complex class defined by a combination of elementary character classes.
///
///
public partial class CharClassSet : CharClass, IEnumerable>
{
public IEnumerable Positives { get; set; }
public 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; private set; }
public List Factors { get; private set; }
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; 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 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; set; }
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) { }
}
}
}