// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace QtVsTools.SyntaxAnalysis
{
using static CharClass.CharSetExprBuilder;
using static CharClassSet;
////////////////////////////////////////////////////////////////////////////////////////////////
///
/// 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 => Positives?.Any() == true;
bool HasNegative => Negatives?.Any() == true;
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)
pattern.Append(HasPositive ? "[" : "[^");
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 ?? Empty();
Negatives = 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] => 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 (expr == null)
continue;
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;
}
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 charClassSet)
classSet = 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.ForEach(x => classSet.Add(x));
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] =>
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] => CharRawLiteral(s);
}
public class CharClassEvalException : RegExprException
{
public CharClassEvalException(string message = null) : base(message) { }
}
}
}