ConstraintBuilder
ConstraintBuilder maintains the stacks that are used in
processing a ConstraintExpression. An OperatorStack
is used to hold operators that are waiting for their
operands to be reorganized. a ConstraintStack holds
input constraints as well as the results of each
operator applied.
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace NUnit.Framework.Constraints
{
[System.Runtime.CompilerServices.NullableContext(1)]
[System.Runtime.CompilerServices.Nullable(0)]
public sealed class ConstraintBuilder : IResolveConstraint
{
[System.Runtime.CompilerServices.Nullable(0)]
private sealed class OperatorStack
{
private readonly Stack<ConstraintOperator> _stack = new Stack<ConstraintOperator>();
public bool Empty => _stack.Count == 0;
public ConstraintOperator Top => _stack.Peek();
public void Push(ConstraintOperator op)
{
_stack.Push(op);
}
public ConstraintOperator Pop()
{
return _stack.Pop();
}
}
[System.Runtime.CompilerServices.Nullable(0)]
public sealed class ConstraintStack
{
private readonly Stack<IConstraint> _stack = new Stack<IConstraint>();
private readonly ConstraintBuilder _builder;
public bool Empty => _stack.Count == 0;
public ConstraintStack(ConstraintBuilder builder)
{
_builder = builder;
}
public void Push(IConstraint constraint)
{
_stack.Push(constraint);
constraint.Builder = _builder;
}
public IConstraint Pop()
{
IConstraint constraint = _stack.Pop();
constraint.Builder = null;
return constraint;
}
}
private readonly OperatorStack _ops;
private readonly ConstraintStack _constraints;
[System.Runtime.CompilerServices.Nullable(2)]
private object _lastPushed;
private bool IsResolvable {
get {
if (!(_lastPushed is Constraint))
return _lastPushed is SelfResolvingOperator;
return true;
}
}
public ConstraintBuilder()
{
_ops = new OperatorStack();
_constraints = new ConstraintStack(this);
}
public void Append(ConstraintOperator op)
{
op.LeftContext = _lastPushed;
if (_lastPushed is ConstraintOperator)
SetTopOperatorRightContext(op);
ReduceOperatorStack(op.LeftPrecedence);
_ops.Push(op);
_lastPushed = op;
}
public void Append(Constraint constraint)
{
if (_lastPushed is ConstraintOperator)
SetTopOperatorRightContext(constraint);
_constraints.Push(constraint);
_lastPushed = constraint;
constraint.Builder = this;
}
private void SetTopOperatorRightContext(object rightContext)
{
int leftPrecedence = _ops.Top.LeftPrecedence;
_ops.Top.RightContext = rightContext;
if (_ops.Top.LeftPrecedence > leftPrecedence) {
ConstraintOperator constraintOperator = _ops.Pop();
ReduceOperatorStack(constraintOperator.LeftPrecedence);
_ops.Push(constraintOperator);
}
}
private void ReduceOperatorStack(int targetPrecedence)
{
while (!_ops.Empty && _ops.Top.RightPrecedence < targetPrecedence) {
_ops.Pop().Reduce(_constraints);
}
}
public IConstraint Resolve()
{
if (!IsResolvable)
throw new InvalidOperationException("A partial expression may not be resolved");
while (!_ops.Empty) {
ConstraintOperator constraintOperator = _ops.Pop();
constraintOperator.Reduce(_constraints);
}
return _constraints.Pop();
}
}
}