460 lines
12 KiB
Markdown
460 lines
12 KiB
Markdown
# Strata.SqlTools.Rules Class Diagram
|
|
|
|
This diagram shows the class hierarchy for the expression system.
|
|
|
|
```mermaid
|
|
classDiagram
|
|
class IVisitable {
|
|
<<interface>>
|
|
+Accept~T~(IVisitor~T~) T
|
|
}
|
|
note for IVisitable "Visitor Pattern Interface<br/>var visitor = new MyRuleVisitor()#59;<br/>var result = expr.Accept(visitor)#59;"
|
|
|
|
class Expression {
|
|
<<abstract>>
|
|
+Accept~T~(IVisitor~T~) T
|
|
}
|
|
|
|
class BoolExpr {
|
|
<<abstract>>
|
|
}
|
|
|
|
class Literal {
|
|
+Value object
|
|
}
|
|
|
|
class LiteralGeneric~TValue~ {
|
|
+Value TValue
|
|
}
|
|
note for LiteralGeneric "Generic Literal<br/>var literal = new Literal<int><br/>{ Value = 100 }#59;"
|
|
|
|
class Property {
|
|
+Expression Expression
|
|
+PropertyName string
|
|
}
|
|
note for Property "Property Access<br/>var prop = new Property<br/>{ PropertyName = #quot;Age#quot; }#59;"
|
|
|
|
class Logical {
|
|
<<abstract>>
|
|
+Left BoolExpr
|
|
+Right BoolExpr
|
|
}
|
|
|
|
class Comparison {
|
|
<<abstract>>
|
|
+Left Expression
|
|
+Right Expression
|
|
+ExpressionType ExpressionType
|
|
}
|
|
|
|
class And
|
|
note for And "AND Logic<br/>var and = new And<br/>{<br/> Left = expr1,<br/> Right = expr2<br/>}#59;"
|
|
|
|
class Or
|
|
note for Or "OR Logic<br/>var or = new Or<br/>{<br/> Left = expr1,<br/> Right = expr2<br/>}#59;"
|
|
|
|
class With
|
|
note for With "WITH Sequential<br/>var with = new With<br/>{<br/> Left = expr1,<br/> Right = expr2<br/>}#59;"
|
|
|
|
class Equal {
|
|
+ExpressionType ExpressionType
|
|
}
|
|
note for Equal "Equality#58; Age == 25<br/>var eq = new Equal<br/>{<br/> Left = new Property<br/> { PropertyName = #quot;Age#quot; },<br/> Right = new NumberLiteral<br/> { Value = 25m }<br/>}#59;"
|
|
|
|
class GreaterThan {
|
|
+ExpressionType ExpressionType
|
|
}
|
|
note for GreaterThan "Comparison#58; Score > 100<br/>var gt = new GreaterThan<br/>{<br/> Left = new Property<br/> { PropertyName = #quot;Score#quot; },<br/> Right = new NumberLiteral<br/> { Value = 100m }<br/>}#59;"
|
|
|
|
class NumberLiteral {
|
|
+Value decimal
|
|
}
|
|
note for NumberLiteral "Number Literal<br/>var num = new NumberLiteral<br/>{ Value = 42.5m }#59;"
|
|
|
|
class StringLiteral {
|
|
+Value string
|
|
}
|
|
note for StringLiteral "String Literal<br/>var str = new StringLiteral<br/>{ Value = #quot;Hello#quot; }#59;"
|
|
|
|
IVisitable <|.. Expression
|
|
Expression <|-- BoolExpr
|
|
Expression <|-- Literal
|
|
Expression <|-- Property
|
|
|
|
BoolExpr <|-- Logical
|
|
BoolExpr <|-- Comparison
|
|
|
|
Literal <|-- LiteralGeneric
|
|
|
|
LiteralGeneric <|-- NumberLiteral
|
|
LiteralGeneric <|-- StringLiteral
|
|
|
|
Logical <|-- And
|
|
Logical <|-- Or
|
|
Logical <|-- With
|
|
|
|
Comparison <|-- Equal
|
|
Comparison <|-- GreaterThan
|
|
```
|
|
|
|
## Class Descriptions
|
|
|
|
### Core Classes
|
|
|
|
- **IVisitable**: Interface for classes that can be visited using the visitor pattern
|
|
- **Expression**: Base abstract class for all expressions
|
|
- **BoolExpr**: Base class for expressions that evaluate to boolean values
|
|
|
|
### Literal Expressions
|
|
|
|
- **Literal**: Represents a literal value
|
|
- **Literal<TValue>**: Generic typed literal expression
|
|
- **NumberLiteral**: Represents numeric literal values (decimal)
|
|
- **StringLiteral**: Represents string literal values
|
|
|
|
### Property Expressions
|
|
|
|
- **Property**: Represents property access in expressions
|
|
|
|
### Logical Expressions
|
|
|
|
- **Logical**: Base class for logical operations (AND, OR, WITH)
|
|
- **And**: Logical AND operation
|
|
- **Or**: Logical OR operation
|
|
- **With**: Sequential WITH operation
|
|
|
|
### Comparison Expressions
|
|
|
|
- **Comparison**: Base class for comparison operations
|
|
- **Equal**: Equality comparison (==)
|
|
- **GreaterThan**: Greater than comparison (>)
|
|
|
|
## C# Usage Examples
|
|
|
|
### Creating Literal Expressions
|
|
|
|
```csharp
|
|
// String literal
|
|
var stringLiteral = new StringLiteral
|
|
{
|
|
Value = "Hello World"
|
|
};
|
|
|
|
// Number literal
|
|
var numberLiteral = new NumberLiteral
|
|
{
|
|
Value = 42.5m
|
|
};
|
|
|
|
// Generic typed literal
|
|
var typedLiteral = new Literal<int>
|
|
{
|
|
Value = 100
|
|
};
|
|
```
|
|
|
|
### Creating Property Expressions
|
|
|
|
```csharp
|
|
// Simple property access
|
|
var propertyExpr = new Property
|
|
{
|
|
PropertyName = "Age"
|
|
};
|
|
|
|
// Property with nested expression
|
|
var nestedPropertyExpr = new Property
|
|
{
|
|
PropertyName = "Address",
|
|
Expression = new Property
|
|
{
|
|
PropertyName = "City"
|
|
}
|
|
};
|
|
```
|
|
|
|
### Creating Comparison Expressions
|
|
|
|
```csharp
|
|
// Equal comparison: Age == 25
|
|
var equalExpr = new Equal
|
|
{
|
|
Left = new Property { PropertyName = "Age" },
|
|
Right = new NumberLiteral { Value = 25m }
|
|
};
|
|
|
|
// Greater than comparison: Score > 100
|
|
var greaterThanExpr = new GreaterThan
|
|
{
|
|
Left = new Property { PropertyName = "Score" },
|
|
Right = new NumberLiteral { Value = 100m }
|
|
};
|
|
```
|
|
|
|
### Creating Logical Expressions
|
|
|
|
```csharp
|
|
// AND expression: Age > 18 AND Status == "Active"
|
|
var andExpr = new And
|
|
{
|
|
Left = new GreaterThan
|
|
{
|
|
Left = new Property { PropertyName = "Age" },
|
|
Right = new NumberLiteral { Value = 18m }
|
|
},
|
|
Right = new Equal
|
|
{
|
|
Left = new Property { PropertyName = "Status" },
|
|
Right = new StringLiteral { Value = "Active" }
|
|
}
|
|
};
|
|
|
|
// OR expression: Type == "Premium" OR Score > 500
|
|
var orExpr = new Or
|
|
{
|
|
Left = new Equal
|
|
{
|
|
Left = new Property { PropertyName = "Type" },
|
|
Right = new StringLiteral { Value = "Premium" }
|
|
},
|
|
Right = new GreaterThan
|
|
{
|
|
Left = new Property { PropertyName = "Score" },
|
|
Right = new NumberLiteral { Value = 500m }
|
|
}
|
|
};
|
|
```
|
|
|
|
### Complex Expression Example
|
|
|
|
```csharp
|
|
// (Age > 18 AND Status == "Active") OR (Type == "Premium" WITH Score > 500)
|
|
var complexExpr = new Or
|
|
{
|
|
Left = new And
|
|
{
|
|
Left = new GreaterThan
|
|
{
|
|
Left = new Property { PropertyName = "Age" },
|
|
Right = new NumberLiteral { Value = 18m }
|
|
},
|
|
Right = new Equal
|
|
{
|
|
Left = new Property { PropertyName = "Status" },
|
|
Right = new StringLiteral { Value = "Active" }
|
|
}
|
|
},
|
|
Right = new With
|
|
{
|
|
Left = new Equal
|
|
{
|
|
Left = new Property { PropertyName = "Type" },
|
|
Right = new StringLiteral { Value = "Premium" }
|
|
},
|
|
Right = new GreaterThan
|
|
{
|
|
Left = new Property { PropertyName = "Score" },
|
|
Right = new NumberLiteral { Value = 500m }
|
|
}
|
|
}
|
|
};
|
|
```
|
|
|
|
### Using the Visitor Pattern
|
|
|
|
```csharp
|
|
// Implement a custom visitor
|
|
public class MyRuleVisitor : IVisitor<string>
|
|
{
|
|
public string Visit(And expression)
|
|
{
|
|
return $"({expression.Left.Accept(this)} AND {expression.Right.Accept(this)})";
|
|
}
|
|
|
|
public string Visit(Equal expression)
|
|
{
|
|
return $"{expression.Left.Accept(this)} == {expression.Right.Accept(this)}";
|
|
}
|
|
|
|
public string Visit(StringLiteral expression)
|
|
{
|
|
return $"\"{expression.Value}\"";
|
|
}
|
|
|
|
// ... implement other Visit methods
|
|
}
|
|
|
|
// Use the visitor
|
|
var visitor = new MyRuleVisitor();
|
|
var result = complexExpr.Accept(visitor);
|
|
Console.WriteLine(result);
|
|
```
|
|
|
|
## Markdown Parser
|
|
|
|
The `Markdown` class provides functionality to parse markdown/LaTeX mathematical expressions and convert them into Expression objects. This is useful for:
|
|
- Documenting rules in markdown format
|
|
- Creating expressions from user-friendly text representations
|
|
- Converting mathematical notation to executable rule expressions
|
|
|
|
### Supported Markdown Delimiters
|
|
|
|
The parser automatically strips these common markdown delimiters:
|
|
- Inline math: `$...$`
|
|
- Block math: `$$...$$`
|
|
- Code fence: ` ```math...``` `
|
|
|
|
### Supported Syntax
|
|
|
|
#### Logical Operators
|
|
- `AND` or `\land` or `\wedge` - Logical AND
|
|
- `OR` or `\lor` or `\vee` - Logical OR
|
|
|
|
#### Comparison Operators
|
|
- `=` - Equality
|
|
- `!=` or `\neq` - Not equal
|
|
- `>` or `\gt` - Greater than
|
|
|
|
#### Literals
|
|
- **Numbers**: `42`, `3.14`
|
|
- **Strings**: `"text"` or `'text'` or `\text{text}`
|
|
- **Booleans**: `true`, `false`
|
|
|
|
#### Properties
|
|
- Simple: `PropertyName`
|
|
- With parameter: `x.PropertyName`
|
|
- LaTeX format: `\text{x.PropertyName}`
|
|
|
|
#### Parentheses
|
|
- Regular: `(...)`
|
|
- LaTeX: `\left(...\right)`
|
|
|
|
### Markdown Parser Usage Examples
|
|
|
|
#### Basic Parsing
|
|
|
|
```csharp
|
|
using Strata.SqlTools.Rules.Rule.Expression;
|
|
|
|
// Parse a simple comparison
|
|
var expr1 = Markdown.Parse("x.Age > 18");
|
|
// Returns: GreaterThan { Left = Property("x", "Age"), Right = NumberLiteral(18) }
|
|
|
|
// Parse with inline math delimiters
|
|
var expr2 = Markdown.Parse("$x.Status = 'active'$");
|
|
// Returns: Equal { Left = Property("x", "Status"), Right = StringLiteral("active") }
|
|
|
|
// Parse with block math delimiters
|
|
var expr3 = Markdown.Parse(@"$$
|
|
user.IsVerified = true
|
|
$$");
|
|
// Returns: Equal { Left = Property("user", "IsVerified"), Right = Literal(true) }
|
|
```
|
|
|
|
#### Parsing Logical Operations
|
|
|
|
```csharp
|
|
// Parse AND expression
|
|
var andExpr = Markdown.Parse("x.Age > 18 AND x.Active = true");
|
|
// Returns: And { Left = GreaterThan(...), Right = Equal(...) }
|
|
|
|
// Parse OR with LaTeX notation
|
|
var orExpr = Markdown.Parse(@"$
|
|
x.Type = 'premium' \lor x.Score > 500
|
|
$");
|
|
// Returns: Or { Left = Equal(...), Right = GreaterThan(...) }
|
|
|
|
// Parse with LaTeX wedge (AND) and vee (OR)
|
|
var complexExpr = Markdown.Parse(@"
|
|
(x.Valid = true \wedge x.Count > 0) \vee y.Override = true
|
|
");
|
|
// Returns: Or { Left = And(...), Right = Equal(...) }
|
|
```
|
|
|
|
#### Parsing Complex Expressions
|
|
|
|
```csharp
|
|
// Complex business rule with nested conditions
|
|
var businessRule = Markdown.Parse(@"$$
|
|
(invoice.TotalCharges > 1000 \land invoice.Status = \text{pending})
|
|
\lor
|
|
(invoice.Priority = \text{urgent} \land invoice.ApprovedBy \neq \text{})
|
|
$$");
|
|
|
|
// Use with visitor pattern
|
|
var visitor = new MyRuleVisitor();
|
|
var result = businessRule.Accept(visitor);
|
|
```
|
|
|
|
#### Safe Parsing with TryParse
|
|
|
|
```csharp
|
|
// Use TryParse for error handling
|
|
if (Markdown.TryParse("x.Age > 18", out var expression))
|
|
{
|
|
Console.WriteLine("Parsed successfully!");
|
|
// Use the expression
|
|
var result = expression.Accept(myVisitor);
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine("Failed to parse expression");
|
|
}
|
|
```
|
|
|
|
#### Real-World Example
|
|
|
|
```csharp
|
|
// Define a rule in markdown documentation
|
|
var ruleMarkdown = @"
|
|
# User Eligibility Rule
|
|
|
|
The user must meet one of the following conditions:
|
|
|
|
\`\`\`math
|
|
(\text{user.Age} > 18 \land \text{user.AccountStatus} = \text{active})
|
|
\lor
|
|
(\text{user.Role} = \text{admin})
|
|
\`\`\`
|
|
";
|
|
|
|
// Extract and parse the math block
|
|
var mathContent = ExtractMathBlock(ruleMarkdown); // Your extraction logic
|
|
var eligibilityRule = Markdown.Parse(mathContent);
|
|
|
|
// Apply the rule
|
|
public class EligibilityChecker : IVisitor<bool>
|
|
{
|
|
private readonly User _user;
|
|
|
|
public EligibilityChecker(User user) => _user = user;
|
|
|
|
public bool VisitAnd(And expression) =>
|
|
expression.Left.Accept(this) && expression.Right.Accept(this);
|
|
|
|
public bool VisitOr(Or expression) =>
|
|
expression.Left.Accept(this) || expression.Right.Accept(this);
|
|
|
|
public bool VisitEqual(Equal expression)
|
|
{
|
|
var left = expression.Left.Accept(new PropertyEvaluator(_user));
|
|
var right = expression.Right.Accept(new LiteralEvaluator());
|
|
return Equals(left, right);
|
|
}
|
|
|
|
// ... other visitor methods
|
|
}
|
|
|
|
// Check eligibility
|
|
var checker = new EligibilityChecker(currentUser);
|
|
bool isEligible = eligibilityRule.Accept(checker);
|
|
```
|
|
|
|
### Benefits of Using Markdown Parser
|
|
|
|
1. **Documentation and Code Alignment**: Keep rule documentation and implementation in sync
|
|
2. **Human-Readable Rules**: Write business rules in a format that non-developers can understand
|
|
3. **LaTeX Support**: Use standard mathematical notation for complex logical expressions
|
|
4. **Easy Testing**: Write test cases using readable markdown expressions
|
|
5. **Version Control Friendly**: Track rule changes in readable text format
|