using System.Collections.Generic;
public interface IKashmirConditional
string When { get; set; }
string Unless { get; set; }
string[] WhenAny { get; set; }
string[] UnlessAny { get; set; }
string[] WhenAll { get; set; }
string[] UnlessAll { get; set; }
IMaybeBooleanReturnType WhenExpression { get; set; }
public interface IMaybeBooleanReturnType {}
public class BoolExpression : IMaybeBooleanReturnType {
public string Value { get; }
public BoolExpression(string value) { Value = value; }
public override string ToString() => Value;
public class NotExpression : IMaybeBooleanReturnType {
public IMaybeBooleanReturnType Value { get; }
public NotExpression(IMaybeBooleanReturnType value) { Value = value; }
public override string ToString() => $"(not {Value})";
public class AndExpression : IMaybeBooleanReturnType {
public IList<IMaybeBooleanReturnType> Values { get; }
public AndExpression(IEnumerable<IMaybeBooleanReturnType> values) { Values = values.ToList(); }
public AndExpression(params IMaybeBooleanReturnType[] values) { Values = values; }
public override string ToString() => $"(and {string.Join(' ', Values)})";
public class OrExpression : IMaybeBooleanReturnType {
public IList<IMaybeBooleanReturnType> Values { get; }
public OrExpression(IEnumerable<IMaybeBooleanReturnType> values) { Values = values.ToList(); }
public OrExpression(params IMaybeBooleanReturnType[] values) { Values = values; }
public override string ToString() => $"(or {string.Join(' ', Values)})";
public class NormalizeFooAttribute: IKashmirConditional
public string When { get; set; }
public string Unless { get; set; }
public string[] WhenAny { get; set; }
public string[] UnlessAny { get; set; }
public string[] WhenAll { get; set; }
public string[] UnlessAll { get; set; }
public IMaybeBooleanReturnType WhenExpression { get; set; }
public static IMaybeBooleanReturnType ParseCondition(string condition)
return new BoolExpression(condition);
public static void SetWhenOnceOrThrow(IKashmirConditional attr, IMaybeBooleanReturnType expression)
if (attr.WhenExpression != null)
throw new InvalidOperationException("Expected `WhenExpression` to be null");
attr.WhenExpression = expression;
public static void InitConditional(IKashmirConditional attr)
SetWhenOnceOrThrow(attr, ParseCondition(attr.When));
SetWhenOnceOrThrow(attr, new NotExpression(
ParseCondition(attr.Unless)));
if (attr.WhenAny != null)
SetWhenOnceOrThrow(attr, new OrExpression(
.Select(ParseCondition)));
if (attr.UnlessAny != null)
SetWhenOnceOrThrow(attr, new OrExpression(
.Select(expr => new NotExpression(expr))));
if (attr.WhenAll != null)
SetWhenOnceOrThrow(attr, new AndExpression(
.Select(ParseCondition)));
if (attr.UnlessAll != null)
SetWhenOnceOrThrow(attr, new AndExpression(
.Select(expr => new NotExpression(expr))));
public static void Main()
var when = new NormalizeFooAttribute
Console.WriteLine("when => {0}", when.WhenExpression);
var unless = new NormalizeFooAttribute
Console.WriteLine("unless => {0}", unless.WhenExpression);
var whenAny = new NormalizeFooAttribute
WhenAny = new[] { @"(eq 1 1)", @"(neq 1 1)"},
InitConditional(whenAny);
Console.WriteLine("whenAny => {0}", whenAny.WhenExpression);
var unlessAny = new NormalizeFooAttribute
UnlessAny = new[] { @"(eq 1 1)", @"(neq 1 1)"},
InitConditional(unlessAny);
Console.WriteLine("unlessAny => {0}", unlessAny.WhenExpression);
var whenAll = new NormalizeFooAttribute
WhenAll = new[] { @"(eq 1 1)", @"(neq 1 1)"},
InitConditional(whenAll);
Console.WriteLine("whenAll => {0}", whenAll.WhenExpression);
var unlessAll = new NormalizeFooAttribute
UnlessAll = new[] { @"(eq 1 1)", @"(neq 1 1)"},
InitConditional(unlessAll);
Console.WriteLine("unlessAll => {0}", unlessAll.WhenExpression);
var willThrow = new NormalizeFooAttribute
UnlessAll = new[] { @"(eq 1 1)", @"(neq 1 1)"},
InitConditional(willThrow);
Console.WriteLine("willThrow => {0}", e.ToString());