Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#224 - Introduce simplification rules #579

Draft
wants to merge 1 commit into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions xFunc.Maths/Analyzers2/ChainRuleBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
namespace xFunc.Maths.Analyzers2;

Check failure on line 1 in xFunc.Maths/Analyzers2/ChainRuleBuilder.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

The file header is missing or not located at the top of the file. (https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1633.md)

Check failure on line 1 in xFunc.Maths/Analyzers2/ChainRuleBuilder.cs

View check run for this annotation

Azure Pipelines / sys27.xFunc

xFunc.Maths/Analyzers2/ChainRuleBuilder.cs#L1

xFunc.Maths/Analyzers2/ChainRuleBuilder.cs(1,1): Error SA1633: The file header is missing or not located at the top of the file. (https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1633.md)

internal class ChainRuleBuilder<TExpression> :
IInitialChainRuleBuilder<TExpression>,
IChainRuleBuilder<TExpression>
where TExpression : IExpression
{
private ChainRule initialRule;

Check failure on line 8 in xFunc.Maths/Analyzers2/ChainRuleBuilder.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

Non-nullable field 'initialRule' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

Check failure on line 8 in xFunc.Maths/Analyzers2/ChainRuleBuilder.cs

View check run for this annotation

Azure Pipelines / sys27.xFunc

xFunc.Maths/Analyzers2/ChainRuleBuilder.cs#L8

xFunc.Maths/Analyzers2/ChainRuleBuilder.cs(8,23): Error CS8618: Non-nullable field 'initialRule' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.
private ChainRule currentRule;

Check failure on line 9 in xFunc.Maths/Analyzers2/ChainRuleBuilder.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

Non-nullable field 'currentRule' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

Check failure on line 9 in xFunc.Maths/Analyzers2/ChainRuleBuilder.cs

View check run for this annotation

Azure Pipelines / sys27.xFunc

xFunc.Maths/Analyzers2/ChainRuleBuilder.cs#L9

xFunc.Maths/Analyzers2/ChainRuleBuilder.cs(9,23): Error CS8618: Non-nullable field 'currentRule' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.

public IChainRuleBuilder<TExpression> WithRule(IRule<TExpression> rule)
{
initialRule = currentRule = new ChainRule(rule);

return this;
}

public IChainRuleBuilder<TExpression> WithNext(IRule<TExpression> next)
{
var chain = new ChainRule(next);
currentRule.SetNext(chain);
currentRule = chain;

return this;
}

public IRule GetRule()
=> initialRule.UnwrapIfEmpty();

private sealed class ChainRule : IRule<TExpression>
{
private readonly IRule<TExpression> current;
private IRule<TExpression>? next;

public ChainRule(IRule<TExpression> rule)
=> current = rule ?? throw new ArgumentNullException(nameof(rule));

public void SetNext(IRule<TExpression> rule)
=> next = rule ?? throw new ArgumentNullException(nameof(rule));

public IRule UnwrapIfEmpty()
=> next is null ? current : this;

public Result Execute(IExpression expression, RuleContext context)
{
var result = current.Execute(expression, context);
if (result.IsHandled() || result.IsReAnalyze())
return result;

if (result.IsContinue())
expression = result.Value;

if (next is not null)
return next.Execute(expression, context);

return Result.NotHandled();
}

public Result Execute(TExpression expression, RuleContext context)
=> Execute(expression as IExpression, context);

public string Name => "Chain Rule";
}
}
17 changes: 17 additions & 0 deletions xFunc.Maths/Analyzers2/ExecutionStep.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace xFunc.Maths.Analyzers2;

Check failure on line 1 in xFunc.Maths/Analyzers2/ExecutionStep.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

The file header is missing or not located at the top of the file. (https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1633.md)

Check failure on line 1 in xFunc.Maths/Analyzers2/ExecutionStep.cs

View check run for this annotation

Azure Pipelines / sys27.xFunc

xFunc.Maths/Analyzers2/ExecutionStep.cs#L1

xFunc.Maths/Analyzers2/ExecutionStep.cs(1,1): Error SA1633: The file header is missing or not located at the top of the file. (https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1633.md)

public readonly struct ExecutionStep
{
public ExecutionStep(IRule rule, IExpression before, IExpression after)
{
Name = rule.Name;
Before = before;
After = after;
}

public string Name { get; }

public IExpression Before { get; }

public IExpression After { get; }
}
8 changes: 8 additions & 0 deletions xFunc.Maths/Analyzers2/IAnalyzer2.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace xFunc.Maths.Analyzers2;

Check failure on line 1 in xFunc.Maths/Analyzers2/IAnalyzer2.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

The file header is missing or not located at the top of the file. (https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1633.md)

public interface IAnalyzer2
{
IExpression Analyze(IExpression expression);

IExpression Analyze(IExpression expression, RuleContext context);
}
7 changes: 7 additions & 0 deletions xFunc.Maths/Analyzers2/IChainRuleBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace xFunc.Maths.Analyzers2;

Check failure on line 1 in xFunc.Maths/Analyzers2/IChainRuleBuilder.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

The file header is missing or not located at the top of the file. (https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1633.md)

internal interface IChainRuleBuilder<out TExpression>
where TExpression : IExpression
{
IChainRuleBuilder<TExpression> WithNext(IRule<TExpression> next);
}
7 changes: 7 additions & 0 deletions xFunc.Maths/Analyzers2/IInitialChainRuleBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace xFunc.Maths.Analyzers2;

Check failure on line 1 in xFunc.Maths/Analyzers2/IInitialChainRuleBuilder.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

The file header is missing or not located at the top of the file. (https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1633.md)

internal interface IInitialChainRuleBuilder<out TExpression>
where TExpression : IExpression
{
IChainRuleBuilder<TExpression> WithRule(IRule<TExpression> rule);
}
8 changes: 8 additions & 0 deletions xFunc.Maths/Analyzers2/IRule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace xFunc.Maths.Analyzers2;

Check failure on line 1 in xFunc.Maths/Analyzers2/IRule.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

The file header is missing or not located at the top of the file. (https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1633.md)

public interface IRule
{
Result Execute(IExpression expression, RuleContext context);

string Name { get; }
}
6 changes: 6 additions & 0 deletions xFunc.Maths/Analyzers2/IRule{TExpression}.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace xFunc.Maths.Analyzers2;

Check failure on line 1 in xFunc.Maths/Analyzers2/IRule{TExpression}.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

The file header is missing or not located at the top of the file. (https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1633.md)

public interface IRule<in TExpression> : IRule
{
Result Execute(TExpression expression, RuleContext context);
}
47 changes: 47 additions & 0 deletions xFunc.Maths/Analyzers2/Result.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System.Diagnostics.CodeAnalysis;

Check failure on line 1 in xFunc.Maths/Analyzers2/Result.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

The file header is missing or not located at the top of the file. (https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1633.md)

namespace xFunc.Maths.Analyzers2;

public readonly struct Result
{
private readonly ResultKind kind;

private Result(ResultKind kind, IExpression? value)
{
this.kind = kind;
Value = value;
}

public static Result NotHandled()
=> new Result(ResultKind.NotHandled, default);

public static Result Handled(IExpression value)
=> new Result(ResultKind.Handled, value);

public static Result Continue(IExpression value)
=> new Result(ResultKind.Continue, value);

public static Result ReAnalyze(IExpression value)
=> new Result(ResultKind.ReAnalyze, value);

private bool IsKind(ResultKind kind)
=> this.kind == kind;

[MemberNotNullWhen(false, nameof(Value))]
public bool IsNotHandled()
=> IsKind(ResultKind.NotHandled);

[MemberNotNullWhen(true, nameof(Value))]
public bool IsHandled()
=> IsKind(ResultKind.Handled);

[MemberNotNullWhen(true, nameof(Value))]
public bool IsContinue()
=> IsKind(ResultKind.Continue);

[MemberNotNullWhen(true, nameof(Value))]
public bool IsReAnalyze()
=> IsKind(ResultKind.ReAnalyze);

public IExpression? Value { get; }
}
9 changes: 9 additions & 0 deletions xFunc.Maths/Analyzers2/ResultKind.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace xFunc.Maths.Analyzers2;

public enum ResultKind
{
NotHandled,
Handled,
Continue,
ReAnalyze,
}
33 changes: 33 additions & 0 deletions xFunc.Maths/Analyzers2/Rule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
namespace xFunc.Maths.Analyzers2;

public abstract class Rule<TExpression> : IRule<TExpression>
where TExpression : IExpression
{
Result IRule.Execute(IExpression expression, RuleContext context)
=> Execute((TExpression)expression, context);

public Result Execute(TExpression expression, RuleContext context)
{
var result = ExecuteInternal(expression, context);
if (result.IsHandled() || result.IsContinue() || result.IsReAnalyze())
context.AddStep(this, expression, result.Value);

return result;
}

protected abstract Result ExecuteInternal(TExpression expression, RuleContext context);

protected static Result Handled(IExpression value)
=> Result.Handled(value);

protected static Result Continue(IExpression value)
=> Result.Continue(value);

protected static Result ReAnalyze(IExpression value)
=> Result.ReAnalyze(value);

protected static Result NotHandled()
=> Result.NotHandled();

public virtual string Name => GetType().Name;
}
21 changes: 21 additions & 0 deletions xFunc.Maths/Analyzers2/RuleContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace xFunc.Maths.Analyzers2;

public class RuleContext
{
private readonly IAnalyzer2 analyzer;
private readonly List<ExecutionStep> steps;

public RuleContext(IAnalyzer2 analyzer)
{
this.analyzer = analyzer;
this.steps = new List<ExecutionStep>();
}

public IExpression Analyze(IExpression expression)
=> analyzer.Analyze(expression, this);

public void AddStep(IRule rule, IExpression before, IExpression after)
=> steps.Add(new ExecutionStep(rule, before, after));

public IEnumerable<ExecutionStep> Steps => steps;
}
16 changes: 16 additions & 0 deletions xFunc.Maths/Analyzers2/RuleStorage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace xFunc.Maths.Analyzers2;

internal class RuleStorage
{
private readonly IReadOnlyDictionary<Type, IRule> rules;

public RuleStorage(IReadOnlyDictionary<Type, IRule> rules)
=> this.rules = rules;

public IRule? GetRule(Type type)
{
rules.TryGetValue(type, out var rule);

return rule;
}
}
29 changes: 29 additions & 0 deletions xFunc.Maths/Analyzers2/RuleStorageBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
namespace xFunc.Maths.Analyzers2;

internal class RuleStorageBuilder
{
private readonly Dictionary<Type, IRule> rules = new Dictionary<Type, IRule>();

public RuleStorageBuilder WithRule<TExpression>(IRule<TExpression> rule)
where TExpression : IExpression
{
rules[typeof(TExpression)] = rule;

return this;
}

public RuleStorageBuilder WithChain<TExpression>(
Action<IInitialChainRuleBuilder<TExpression>> builder)
where TExpression : IExpression
{
var ruleBuilder = new ChainRuleBuilder<TExpression>();
builder(ruleBuilder);
var rule = ruleBuilder.GetRule();
rules[typeof(TExpression)] = rule;

return this;
}

public RuleStorage Build()
=> new RuleStorage(rules);
}
13 changes: 13 additions & 0 deletions xFunc.Maths/Analyzers2/Rules/AbsRules/AbsNestedRule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace xFunc.Maths.Analyzers2.Rules.AbsRules;

public class AbsNestedRule : Rule<Abs>
{
protected override Result ExecuteInternal(Abs expression, RuleContext context)
=> expression.Argument switch
{
Abs abs => Handled(abs),
_ => NotHandled(),
};

public override string Name => "Abs Nested Rule";
}
13 changes: 13 additions & 0 deletions xFunc.Maths/Analyzers2/Rules/AbsRules/AbsUnaryMinusRule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace xFunc.Maths.Analyzers2.Rules.AbsRules;

public class AbsUnaryMinusRule : Rule<Abs>
{
protected override Result ExecuteInternal(Abs expression, RuleContext context)
=> expression.Argument switch
{
UnaryMinus minus => Handled(minus.Argument),
_ => NotHandled(),
};

public override string Name => "Abs Unary Minus";
}
9 changes: 9 additions & 0 deletions xFunc.Maths/Analyzers2/Rules/AbsRules/AbsUnaryRule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace xFunc.Maths.Analyzers2.Rules.AbsRules;

public class AbsUnaryRule : UnaryRule<Abs>
{
protected override Abs Create(IExpression argument)
=> new Abs(argument);

public override string Name => "Abs Unary Rule";
}
9 changes: 9 additions & 0 deletions xFunc.Maths/Analyzers2/Rules/AddRules/AddBinaryRule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace xFunc.Maths.Analyzers2.Rules.AddRules;

public class AddBinaryRule : BinaryRule<Add>
{
protected override Add Create(IExpression left, IExpression right)
=> new Add(left, right);

public override string Name => "Add Binary Rule";
}
Loading