chore: initial git load of code space

This commit is contained in:
Thom Lamb
2026-05-12 08:52:33 -05:00
parent 9abada692f
commit 5e467bcc9c
384 changed files with 65960 additions and 2 deletions
@@ -0,0 +1,20 @@
using System.Diagnostics;
namespace Strata.SqlTools.Rules.Rule.Expression;
/// <summary>
/// Represents a logical AND operation between two BoolExpr expressions.
/// </summary>
[DebuggerDisplay("{Left} AND {Right}")]
public class And : Logical
{
/// <summary>
/// Initializes a new instance of the <see cref="And"/> class.
/// </summary>
/// <param name="left">The left operand.</param>
/// <param name="right">The right operand.</param>
public And(BoolExpr left, BoolExpr right) : base(left, right) { }
/// <inheritdoc/>
public override T Accept<T>(IVisitor<T> visitor) => visitor.VisitAnd(this);
}
@@ -0,0 +1,61 @@
namespace Strata.SqlTools.Rules.Rule.Expression;
/// <summary>
/// Represents an ANY expression that checks if any element in a collection satisfies a condition.
/// </summary>
public class Any : BoolExpr
{
/// <summary>
/// Gets the collection property being evaluated.
/// </summary>
public CollectionProperty CollectionProperty { get; }
/// <summary>
/// Gets the BoolExpr expression that defines the condition to check.
/// </summary>
public BoolExpr BoolExpr { get; }
/// <summary>
/// Gets the parameter used in the predicate expression.
/// </summary>
public Parameter PredicateParameter { get; }
/// <summary>
/// Initializes a new instance of the <see cref="Any"/> class with a function.
/// </summary>
/// <param name="collectionProperty">The collection property to evaluate.</param>
/// <param name="func">A function that defines the condition to check for each element.</param>
public Any(CollectionProperty collectionProperty, Func<Parameter, BoolExpr> func)
{
CollectionProperty = collectionProperty;
PredicateParameter = new Parameter("p");
BoolExpr = func(PredicateParameter);
}
/// <summary>
/// Initializes a new instance of the <see cref="Any"/> class with a BoolExpr expression.
/// </summary>
/// <param name="collectionProperty">The collection property to evaluate.</param>
/// <param name="boolExpr">The BoolExpr expression defining the condition.</param>
public Any(CollectionProperty collectionProperty, BoolExpr boolExpr)
: this(collectionProperty, boolExpr, new Parameter("p"))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Any"/> class.
/// </summary>
/// <param name="collectionProperty">The collection property to evaluate.</param>
/// <param name="boolExpr">The BoolExpr expression defining the condition.</param>
/// <param name="predicateParameter">The parameter used in the predicate expression.</param>
public Any(CollectionProperty collectionProperty, BoolExpr boolExpr, Parameter predicateParameter)
{
CollectionProperty = collectionProperty;
BoolExpr = boolExpr;
PredicateParameter = predicateParameter;
}
/// <inheritdoc/>
public override T Accept<T>(IVisitor<T> visitor) => visitor.VisitAny(this);
}
@@ -0,0 +1,23 @@
namespace Strata.SqlTools.Rules.Rule.Expression;
/// <summary>
/// Represents an expression that evaluates to a BoolExpr value (true/false).
/// </summary>
public abstract class BoolExpr : Expression
{
/// <summary>
/// Creates a logical AND expression combining two BoolExpr expressions.
/// </summary>
/// <param name="left">The left operand.</param>
/// <param name="right">The right operand.</param>
/// <returns>An AND expression combining both operands.</returns>
public static BoolExpr operator &(BoolExpr left, BoolExpr right) => new And(left, right);
/// <summary>
/// Creates a logical OR expression combining two BoolExpr expressions.
/// </summary>
/// <param name="left">The left operand.</param>
/// <param name="right">The right operand.</param>
/// <returns>An OR expression combining both operands.</returns>
public static BoolExpr operator |(BoolExpr left, BoolExpr right) => new Or(left, right);
}
@@ -0,0 +1,32 @@
namespace Strata.SqlTools.Rules.Rule.Expression;
/// <summary>
/// Represents a collection property access in a rule expression.
/// </summary>
public class CollectionProperty : Property
{
/// <summary>
/// Initializes a new instance of the <see cref="CollectionProperty"/> class.
/// </summary>
/// <param name="expression">The containing expression.</param>
/// <param name="propertyName">The name of the collection property.</param>
public CollectionProperty(Expression? expression, string propertyName) : base(expression, propertyName)
{
}
/// <inheritdoc/>
public override T Accept<T>(IVisitor<T> visitor) => visitor.VisitCollectionProperty(this);
/// <summary>
/// Creates an ANY expression that checks if any element in the collection satisfies a condition.
/// </summary>
/// <param name="func">A function that defines the condition to check for each element.</param>
/// <returns>An ANY expression.</returns>
public Any Any(Func<Parameter, BoolExpr> func)
{
var parameter = new Parameter("p");
var BoolExpr = func(parameter);
return new Any(this, BoolExpr, parameter);
}
}
@@ -0,0 +1,64 @@
using System.Diagnostics;
namespace Strata.SqlTools.Rules.Rule.Expression;
/// <summary>
/// Represents a comparison operation between two expressions.
/// </summary>
[DebuggerDisplay("{Left} {Type} {Right}")]
public abstract class Comparison : BoolExpr, IBinary
{
/// <summary>
/// Gets the left operand of the comparison.
/// </summary>
public Expression Left { get; }
/// <summary>
/// Gets the right operand of the comparison.
/// </summary>
public Expression Right { get; }
/// <summary>
/// Gets the type of comparison operation.
/// </summary>
public abstract Type Type { get; }
/// <summary>
/// Initializes a new instance of the <see cref="Comparison"/> class.
/// </summary>
/// <param name="left">The left operand.</param>
/// <param name="right">The right operand.</param>
protected Comparison(Expression left, Expression right)
{
Left = left;
Right = right;
}
/// <summary>
/// Creates a new comparison expression with updated operands.
/// </summary>
/// <param name="left">The new left operand.</param>
/// <param name="right">The new right operand.</param>
/// <returns>A new comparison expression or this instance if operands are unchanged.</returns>
public Expression Update(Expression left, Expression right)
{
if (ReferenceEquals(left, Left) && ReferenceEquals(right, Right))
{
return this;
}
return Create(left, right, Type);
}
private static Comparison Create(Expression left, Expression right, Type Type)
{
return Type switch
{
Type.Equal => new Equal(left, right),
Type.NotEqual => new NotEqual(left, right),
Type.GreaterThan => new GreaterThan(left, right),
_ => throw new NotImplementedException("not yet")
};
}
}
@@ -0,0 +1,20 @@
namespace Strata.SqlTools.Rules.Rule.Expression;
/// <summary>
/// Represents an equality comparison between two expressions.
/// </summary>
public class Equal : Comparison
{
/// <inheritdoc/>
public override Type Type => Type.Equal;
/// <summary>
/// Initializes a new instance of the <see cref="Equal"/> class.
/// </summary>
/// <param name="left">The left operand.</param>
/// <param name="right">The right operand.</param>
public Equal(Expression left, Expression right) : base(left, right) { }
/// <inheritdoc/>
public override T Accept<T>(IVisitor<T> visitor) => visitor.VisitEquals(this);
}
@@ -0,0 +1,45 @@
namespace Strata.SqlTools.Rules.Rule.Expression;
/// <summary>
/// Provides implicit conversion operators and comparison operators for rule expressions.
/// </summary>
#pragma warning disable CS0660, CS0661
public partial class Expression
#pragma warning restore CS0660, CS0661
{
/// <summary>
/// Implicitly converts a decimal value to a rule expression.
/// </summary>
/// <param name="value">The decimal value to convert.</param>
public static implicit operator Expression(decimal value) => new NumberLiteral(value);
/// <summary>
/// Implicitly converts a string value to a rule expression.
/// </summary>
/// <param name="value">The string value to convert.</param>
public static implicit operator Expression(string value) => new StringLiteral(value);
/// <summary>
/// Creates an equality comparison rule expression.
/// </summary>
/// <param name="left">The left operand.</param>
/// <param name="right">The right operand.</param>
/// <returns>An equality comparison rule expression.</returns>
public static Comparison operator ==(Expression left, Expression right) => new Equal(left, right);
/// <summary>
/// Creates a not-equal comparison rule expression.
/// </summary>
/// <param name="left">The left operand.</param>
/// <param name="right">The right operand.</param>
/// <returns>A not-equal comparison rule expression.</returns>
public static Comparison operator !=(Expression left, Expression right) => new NotEqual(left, right);
/// <summary>
/// Creates an equality comparison rule expression.
/// </summary>
/// <param name="left">The left operand.</param>
/// <param name="right">The right operand.</param>
/// <returns>An equality comparison rule expression.</returns>
public static Equal Equal(Expression left, Expression right) => new(left, right);
}
@@ -0,0 +1,17 @@
namespace Strata.SqlTools.Rules.Rule.Expression;
/// <summary>
/// Base class for all rule expressions.
/// </summary>
#pragma warning disable CS0660, CS0661
public abstract partial class Expression : IVisitable
#pragma warning restore CS0660, CS0661
{
/// <summary>
/// Accepts a visitor and allows it to process this rule expression.
/// </summary>
/// <typeparam name="T">The return type of the visitor.</typeparam>
/// <param name="visitor">The visitor to accept.</param>
/// <returns>The result of the visitor's processing.</returns>
public abstract T Accept<T>(IVisitor<T> visitor);
}
@@ -0,0 +1,20 @@
namespace Strata.SqlTools.Rules.Rule.Expression;
/// <summary>
/// Represents a greater-than comparison between two expressions.
/// </summary>
public class GreaterThan : Comparison
{
/// <inheritdoc/>
public override Type Type => Type.GreaterThan;
/// <summary>
/// Initializes a new instance of the <see cref="GreaterThan"/> class.
/// </summary>
/// <param name="left">The left operand.</param>
/// <param name="right">The right operand.</param>
public GreaterThan(Expression left, Expression right) : base(left, right) { }
/// <inheritdoc/>
public override T Accept<T>(IVisitor<T> visitor) => visitor.VisitGreaterThan(this);
}
@@ -0,0 +1,22 @@
namespace Strata.SqlTools.Rules.Rule.Expression;
/// <summary>
/// Represents a binary rule expression with left and right operands.
/// </summary>
public interface IBinary : IVisitable
{
/// <summary>
/// Gets the left operand.
/// </summary>
public Expression Left { get; }
/// <summary>
/// Gets the right operand.
/// </summary>
public Expression Right { get; }
/// <summary>
/// Gets the type of rule expression.
/// </summary>
public Type Type { get; }
}
@@ -0,0 +1,15 @@
namespace Strata.SqlTools.Rules.Rule.Expression;
/// <summary>
/// Represents an object that can be visited by a rule visitor implementing the visitor pattern.
/// </summary>
public interface IVisitable
{
/// <summary>
/// Accepts a visitor and allows it to process this visitable object.
/// </summary>
/// <typeparam name="T">The return type of the visitor.</typeparam>
/// <param name="visitor">The visitor to accept.</param>
/// <returns>The result of the visitor's processing.</returns>
T Accept<T>(IVisitor<T> visitor);
}
@@ -0,0 +1,24 @@
using System.Diagnostics;
namespace Strata.SqlTools.Rules.Rule.Expression;
/// <summary>
/// Represents a literal value in a rule expression.
/// </summary>
[DebuggerDisplay("\\{{Value}\\}")]
public class Literal : Expression
{
/// <summary>
/// Gets the literal value.
/// </summary>
public object Value { get; }
/// <summary>
/// Initializes a new instance of the <see cref="Literal"/> class.
/// </summary>
/// <param name="value">The literal value.</param>
public Literal(object value) => Value = value;
/// <inheritdoc/>
public override T Accept<T>(IVisitor<T> visitor) => visitor.VisitLiteral(this);
}
@@ -0,0 +1,20 @@
namespace Strata.SqlTools.Rules.Rule.Expression;
/// <summary>
/// Base class for typed literal rule expressions.
/// </summary>
/// <typeparam name="TValue">The type of the literal value.</typeparam>
public abstract class Literal<TValue> : Literal
where TValue : notnull
{
/// <summary>
/// Gets the strongly-typed literal value.
/// </summary>
public new TValue Value { get; }
/// <summary>
/// Initializes a new instance of the <see cref="Literal{TValue}"/> class.
/// </summary>
/// <param name="value">The literal value.</param>
protected Literal(TValue value) : base(value) => Value = value;
}
@@ -0,0 +1,28 @@
namespace Strata.SqlTools.Rules.Rule.Expression;
/// <summary>
/// Represents a logical operation on two BoolExpr input expressions (e.g., AND, OR).
/// </summary>
public abstract class Logical : BoolExpr
{
/// <summary>
/// Gets the left operand of the logical expression.
/// </summary>
public BoolExpr Left { get; }
/// <summary>
/// Gets the right operand of the logical expression.
/// </summary>
public BoolExpr Right { get; }
/// <summary>
/// Initializes a new instance of the <see cref="Logical"/> class.
/// </summary>
/// <param name="left">The left operand.</param>
/// <param name="right">The right operand.</param>
protected Logical(BoolExpr left, BoolExpr right)
{
Left = left;
Right = right;
}
}
@@ -0,0 +1,398 @@
using System.Text.RegularExpressions;
namespace Strata.SqlTools.Rules.Rule.Expression;
/// <summary>
/// Parses markdown/LaTeX mathematical expressions and converts them to Expression objects.
/// Supports parsing of logical operations, comparisons, properties, and literals.
/// </summary>
public static class Markdown
{
private static readonly Dictionary<string, Func<Expression, Expression, BoolExpr>> LogicalOperators = new()
{
{ "\\land", (left, right) => new And((BoolExpr)left, (BoolExpr)right) },
{ "\\lor", (left, right) => new Or((BoolExpr)left, (BoolExpr)right) },
{ "\\wedge", (left, right) => new And((BoolExpr)left, (BoolExpr)right) },
{ "\\vee", (left, right) => new Or((BoolExpr)left, (BoolExpr)right) },
{ "AND", (left, right) => new And((BoolExpr)left, (BoolExpr)right) },
{ "OR", (left, right) => new Or((BoolExpr)left, (BoolExpr)right) },
};
private static readonly Dictionary<string, Func<Expression, Expression, Comparison>> ComparisonOperators = new()
{
{ "=", (left, right) => new Equal(left, right) },
{ "\\neq", (left, right) => new NotEqual(left, right) },
{ "!=", (left, right) => new NotEqual(left, right) },
{ ">", (left, right) => new GreaterThan(left, right) },
{ "\\gt", (left, right) => new GreaterThan(left, right) },
};
/// <summary>
/// Parses a markdown/LaTeX string into an Expression object.
/// </summary>
/// <param name="markdown">The markdown/LaTeX string to parse.</param>
/// <returns>The parsed Expression object.</returns>
/// <exception cref="ArgumentException">Thrown when the markdown cannot be parsed.</exception>
public static Expression Parse(string markdown)
{
if (string.IsNullOrWhiteSpace(markdown))
{
throw new ArgumentException("Markdown cannot be null or empty", nameof(markdown));
}
// Remove common markdown delimiters
markdown = markdown.Trim();
markdown = StripMarkdownDelimiters(markdown);
return ParseExpression(markdown);
}
/// <summary>
/// Attempts to parse a markdown/LaTeX string into an Expression object.
/// </summary>
/// <param name="markdown">The markdown/LaTeX string to parse.</param>
/// <param name="expression">The parsed Expression object if successful.</param>
/// <returns>True if parsing was successful, false otherwise.</returns>
public static bool TryParse(string markdown, out Expression? expression)
{
try
{
expression = Parse(markdown);
return true;
}
catch
{
expression = null;
return false;
}
}
private static string StripMarkdownDelimiters(string text)
{
// Remove $...$ or $$...$$ delimiters
text = Regex.Replace(text, @"^\$\$?\s*", "");
text = Regex.Replace(text, @"\s*\$\$?$", "");
// Remove ```math...``` code fence
text = Regex.Replace(text, @"^```math\s*", "", RegexOptions.Multiline);
text = Regex.Replace(text, @"\s*```$", "", RegexOptions.Multiline);
return text.Trim();
}
private static Expression ParseExpression(string text)
{
text = text.Trim();
// Try to parse logical operations (lowest precedence)
var logicalExpr = TryParseLogicalOperation(text);
if (logicalExpr is not null)
{
return logicalExpr;
}
// Try to parse comparison operations
var comparisonExpr = TryParseComparison(text);
if (comparisonExpr is not null)
{
return comparisonExpr;
}
// Handle parentheses
var parenthesisExpr = TryParseParentheses(text);
if (parenthesisExpr is not null)
{
return parenthesisExpr;
}
// Parse property, literal, or other atomic expressions
return ParseAtomicExpression(text);
}
private static Expression? TryParseLogicalOperation(string text)
{
foreach (var op in LogicalOperators.Keys)
{
var parts = SplitByOperator(text, op);
if (parts.Length == 2)
{
var left = ParseExpression(parts[0]);
var right = ParseExpression(parts[1]);
return LogicalOperators[op](left, right);
}
}
return null;
}
private static Expression? TryParseComparison(string text)
{
foreach (var op in ComparisonOperators.Keys)
{
var parts = SplitByOperator(text, op);
if (parts.Length == 2)
{
var left = ParseExpression(parts[0]);
var right = ParseExpression(parts[1]);
return ComparisonOperators[op](left, right);
}
}
return null;
}
private static Expression? TryParseParentheses(string text)
{
// Handle regular parentheses
if (text.StartsWith('(') && text.EndsWith(')'))
{
var inner = text.Substring(1, text.Length - 2);
if (IsBalanced(inner))
{
return ParseExpression(inner);
}
}
// Handle LaTeX \left( and \right)
if (text.StartsWith("\\left(") && text.EndsWith("\\right)"))
{
var inner = text.Substring(6, text.Length - 13);
if (IsBalanced(inner))
{
return ParseExpression(inner);
}
}
return null;
}
private static Expression ParseAtomicExpression(string text)
{
// Try parsing as property access
var propertyExpr = TryParseProperty(text);
if (propertyExpr is not null)
{
return propertyExpr;
}
// Try parsing as literal
var literalExpr = TryParseLiteral(text);
if (literalExpr is not null)
{
return literalExpr;
}
throw new ArgumentException($"Unable to parse expression: {text}");
}
private static Expression? TryParseProperty(string text)
{
// Parse property access (e.g., x.PropertyName or \text{x.PropertyName})
var propertyMatch = Regex.Match(text, @"^([a-zA-Z_][a-zA-Z0-9_]*)\.([a-zA-Z_][a-zA-Z0-9_]*)$");
if (propertyMatch.Success)
{
return new Property(propertyMatch.Groups[1].Value, propertyMatch.Groups[2].Value);
}
// Parse \text{...} property access
var textMatch = Regex.Match(text, @"^\\text\{([^}]+)\}$");
if (textMatch.Success)
{
var textContent = textMatch.Groups[1].Value;
var propMatch = Regex.Match(textContent, @"^([a-zA-Z_][a-zA-Z0-9_]*)\.([a-zA-Z_][a-zA-Z0-9_]*)$");
if (propMatch.Success)
{
return new Property(propMatch.Groups[1].Value, propMatch.Groups[2].Value);
}
// Check for boolean literals in \text{} format
if (textContent.Equals("true", StringComparison.OrdinalIgnoreCase))
{
return new Literal(true);
}
if (textContent.Equals("false", StringComparison.OrdinalIgnoreCase))
{
return new Literal(false);
}
// Single property name
if (Regex.IsMatch(textContent, @"^[a-zA-Z_][a-zA-Z0-9_]*$"))
{
return new Property(textContent);
}
// String literal
return new StringLiteral(textContent);
}
// Check for boolean literals before simple property
if (text.Equals("true", StringComparison.OrdinalIgnoreCase))
{
return new Literal(true);
}
if (text.Equals("false", StringComparison.OrdinalIgnoreCase))
{
return new Literal(false);
}
// Parse simple property without parameter
if (Regex.IsMatch(text, @"^[a-zA-Z_][a-zA-Z0-9_]*$"))
{
return new Property(text);
}
return null;
}
private static Expression? TryParseLiteral(string text)
{
// Parse string literals (quoted)
var stringMatch = Regex.Match(text, @"^[""'](.+?)[""']$");
if (stringMatch.Success)
{
return new StringLiteral(stringMatch.Groups[1].Value);
}
// Parse empty string literals
if (text == "\"\"" || text == "''")
{
return new StringLiteral(string.Empty);
}
// Parse numeric literals
if (int.TryParse(text, out var intValue))
{
return new NumberLiteral(intValue);
}
if (decimal.TryParse(text, out var decimalValue))
{
return new NumberLiteral(decimalValue);
}
return null;
}
private static string[] SplitByOperator(string text, string op)
{
var result = new List<string>();
int depth = 0;
int lastIndex = 0;
int i = 0;
while (i < text.Length)
{
i = ProcessParentheses(text, i, ref depth);
if (i >= text.Length)
{
break;
}
// Check if we found the operator at depth 0
if (depth == 0 && i + op.Length <= text.Length && TryMatchOperator(text, i, op))
{
result.Add(text.Substring(lastIndex, i - lastIndex).Trim());
lastIndex = i + op.Length;
i += op.Length;
continue;
}
i++;
}
if (result.Count == 0)
{
return new[] { text };
}
result.Add(text.Substring(lastIndex).Trim());
return result.ToArray();
}
private static int ProcessParentheses(string text, int index, ref int depth)
{
// Track parentheses depth
if (text[index] == '(' || (index + 5 < text.Length && text.Substring(index, 6) == "\\left("))
{
depth++;
if (text[index] == '\\')
{
return index + 6;
}
else
{
return index + 1;
}
}
if (text[index] == ')' || (index + 6 < text.Length && text.Substring(index, 7) == "\\right)"))
{
depth--;
if (text[index] == '\\')
{
return index + 7;
}
else
{
return index + 1;
}
}
return index;
}
private static bool TryMatchOperator(string text, int index, string op)
{
var substring = text.Substring(index, op.Length);
if (substring != op)
{
return false;
}
// Make sure it's a separate operator, not part of a larger token
bool validBefore = (index == 0 || char.IsWhiteSpace(text[index - 1]) || text[index] == '\\');
bool validAfter = (index + op.Length >= text.Length || char.IsWhiteSpace(text[index + op.Length]));
return validBefore && validAfter;
}
private static bool IsBalanced(string text)
{
int depth = 0;
int i = 0;
while (i < text.Length)
{
if (text[i] == '(')
{
depth++;
}
else if (text[i] == ')')
{
depth--;
if (depth < 0)
{
return false;
}
}
else if (i + 5 < text.Length && text.Substring(i, 6) == "\\left(")
{
depth++;
i += 5;
}
else if (i + 6 < text.Length && text.Substring(i, 7) == "\\right)")
{
depth--;
if (depth < 0)
{
return false;
}
i += 6;
}
i++;
}
return depth == 0;
}
}
@@ -0,0 +1,20 @@
namespace Strata.SqlTools.Rules.Rule.Expression;
/// <summary>
/// Represents a not-equal comparison between two expressions.
/// </summary>
public class NotEqual : Comparison
{
/// <inheritdoc/>
public override Type Type => Type.NotEqual;
/// <summary>
/// Initializes a new instance of the <see cref="NotEqual"/> class.
/// </summary>
/// <param name="left">The left operand.</param>
/// <param name="right">The right operand.</param>
public NotEqual(Expression left, Expression right) : base(left, right) { }
/// <inheritdoc/>
public override T Accept<T>(IVisitor<T> visitor) => visitor.VisitNotEquals(this);
}
@@ -0,0 +1,19 @@
namespace Strata.SqlTools.Rules.Rule.Expression;
/// <summary>
/// Represents a numeric literal value in a rule expression.
/// </summary>
public class NumberLiteral : Literal<decimal>
{
/// <summary>
/// Initializes a new instance of the <see cref=" NumberLiteral"/> class.
/// </summary>
/// <param name="value">The numeric value.</param>
public NumberLiteral(decimal value) : base(value) { }
/// <summary>
/// Implicitly converts a <see cref=" NumberLiteral"/> to a decimal value.
/// </summary>
/// <param name="numberExp">The number expression to convert.</param>
public static implicit operator decimal(NumberLiteral numberExp) => numberExp.Value;
}
@@ -0,0 +1,20 @@
using System.Diagnostics;
namespace Strata.SqlTools.Rules.Rule.Expression;
/// <summary>
/// Represents a logical OR operation between two BoolExpr expressions.
/// </summary>
[DebuggerDisplay("{Left} OR {Right}")]
public class Or : Logical
{
/// <summary>
/// Initializes a new instance of the <see cref="Or"/> class.
/// </summary>
/// <param name="left">The left operand.</param>
/// <param name="right">The right operand.</param>
public Or(BoolExpr left, BoolExpr right) : base(left, right) { }
/// <inheritdoc/>
public override T Accept<T>(IVisitor<T> visitor) => visitor.VisitOr(this);
}
@@ -0,0 +1,38 @@
using System.Diagnostics;
namespace Strata.SqlTools.Rules.Rule.Expression;
/// <summary>
/// Represents a parameter in a rule expression.
/// </summary>
[DebuggerDisplay("{ParameterName}")]
public class Parameter : Expression
{
/// <summary>
/// Gets the name of the parameter.
/// </summary>
public string ParameterName { get; }
/// <summary>
/// Initializes a new instance of the <see cref="Parameter"/> class.
/// </summary>
/// <param name="parameterName">The name of the parameter.</param>
public Parameter(string parameterName) => ParameterName = parameterName;
/// <inheritdoc/>
public override T Accept<T>(IVisitor<T> visitor) => visitor.VisitParameter(this);
/// <summary>
/// Creates a property expression for accessing a property on this parameter.
/// </summary>
/// <param name="propertyName">The name of the property.</param>
/// <returns>A property expression.</returns>
public Property Property(string propertyName) => new(this, propertyName);
/// <summary>
/// Creates a collection property expression for accessing a collection property on this parameter.
/// </summary>
/// <param name="collectionPropertyName">The name of the collection property.</param>
/// <returns>A collection property expression.</returns>
public CollectionProperty CollectionProperty(string collectionPropertyName) => new(this, collectionPropertyName);
}
@@ -0,0 +1,55 @@
using System.Diagnostics;
namespace Strata.SqlTools.Rules.Rule.Expression;
/// <summary>
/// Represents a property access in a rule expression.
/// </summary>
[DebuggerDisplay("\\{{Expression,nq}.{PropertyName,nq}\\}")]
public class Property : Expression
{
/// <summary>
/// Gets the containing object of the field or property.
/// </summary>
public Expression? Expression { get; }
/// <summary>
/// Gets the name of the property.
/// </summary>
public string PropertyName { get; }
/// <summary>
/// Initializes a new instance of the <see cref="Property"/> class with no containing expression.
/// </summary>
/// <param name="propertyName">The name of the property.</param>
public Property(string propertyName) : this((Expression?)null, propertyName)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Property"/> class with a parameter name.
/// </summary>
/// <param name="parameterName">The name of the parameter.</param>
/// <param name="propertyName">The name of the property.</param>
public Property(string parameterName, string propertyName) : this(new Parameter(parameterName), propertyName)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Property"/> class.
/// </summary>
/// <param name="expression">The containing expression.</param>
/// <param name="propertyName">The name of the property.</param>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="propertyName"/> is null.</exception>
public Property(Expression? expression, string propertyName)
{
Expression = expression;
// maybe do some regex validation for args to ensure it's not a bogus name (no whitespace, no punctuation marks, etc)
PropertyName = propertyName ?? throw new ArgumentNullException(nameof(propertyName));
}
/// <inheritdoc/>
public override T Accept<T>(IVisitor<T> visitor) => visitor.VisitProperty(this);
}
@@ -0,0 +1,13 @@
namespace Strata.SqlTools.Rules.Rule.Expression;
/// <summary>
/// Represents a string literal value in a rule expression.
/// </summary>
public class StringLiteral : Literal<string>
{
/// <summary>
/// Initializes a new instance of the <see cref="StringLiteral"/> class.
/// </summary>
/// <param name="value">The string value.</param>
public StringLiteral(string value) : base(value) { }
}
@@ -0,0 +1,27 @@
namespace Strata.SqlTools.Rules.Rule.Expression;
/// <summary>
/// Defines the types of rule expressions for comparisons and operations.
/// </summary>
public enum Type
{
/// <summary>Equality comparison.</summary>
Equal,
/// <summary>Inequality comparison.</summary>
NotEqual,
/// <summary>Greater than comparison.</summary>
GreaterThan,
/// <summary>Greater than or equal comparison.</summary>
GreaterThanOrEqual,
/// <summary>Less than comparison.</summary>
LessThan,
/// <summary>Less than or equal comparison.</summary>
LessThanOrEqual,
/// <summary>In operation (value in set).</summary>
In,
/// <summary>None equal operation.</summary>
NoneEqual,
/// <summary>Exclude operation.</summary>
Exclude,
}
@@ -0,0 +1,20 @@
using System.Diagnostics;
namespace Strata.SqlTools.Rules.Rule.Expression;
/// <summary>
/// Represents a WITH operation for sequential rule evaluation.
/// </summary>
[DebuggerDisplay("{Left} WITH {Right}")]
public class With : Logical
{
/// <summary>
/// Initializes a new instance of the <see cref="With"/> class.
/// </summary>
/// <param name="left">The left operand.</param>
/// <param name="right">The right operand.</param>
public With(BoolExpr left, BoolExpr right) : base(left, right) { }
/// <inheritdoc/>
public override T Accept<T>(IVisitor<T> visitor) => visitor.VisitWith(this);
}