Skip to content

Commit

Permalink
Implement ExpressionModifierParser and DateModifierParser. Still need…
Browse files Browse the repository at this point in the history
… to make it all configurable however.
  • Loading branch information
askonomm committed Oct 23, 2024
1 parent 1d5eded commit bf27401
Show file tree
Hide file tree
Showing 12 changed files with 298 additions and 181 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ obj/
/packages/
riderModule.iml
/_ReSharper.Caches/\
/.idea/
/.idea/
Htmt.sln.DotSettings.user
8 changes: 4 additions & 4 deletions Htmt/AttributeParsers/GenericValueAttributeParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,23 @@ namespace Htmt.AttributeParsers;
public class GenericValueAttributeParser : IAttributeParser
{
public string XTag => "//*[@*[starts-with(name(), 'x:')]]";

public void Parse(XmlDocument xml, Dictionary<string, object?> data, XmlNodeList? nodes)
{
// No nodes found
if (nodes == null || nodes.Count == 0)
{
return;
}

foreach (var node in nodes)
{
if (node is not XmlElement n) continue;

var attributes = n.Attributes.Cast<XmlAttribute>()
.Where(a => a.Name.StartsWith("x:"))
.ToList();

foreach (var attr in attributes)
{
var val = n.GetAttribute(attr.Name);
Expand Down
10 changes: 5 additions & 5 deletions Htmt/AttributeParsers/IfAttributeParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Htmt.AttributeParsers;
public class IfAttributeParser : IAttributeParser
{
public string XTag => "//*[@x:if]";

public void Parse(XmlDocument xml, Dictionary<string, object?> data, XmlNodeList? nodes)
{
// No nodes found
Expand All @@ -21,7 +21,7 @@ public void Parse(XmlDocument xml, Dictionary<string, object?> data, XmlNodeList

var key = n.GetAttribute("x:if");
n.RemoveAttribute("x:if");

// if key is a single word, we just check for a truthy value
if (!key.Contains(' '))
{
Expand Down Expand Up @@ -51,12 +51,12 @@ public void Parse(XmlDocument xml, Dictionary<string, object?> data, XmlNodeList
n.ParentNode?.RemoveChild(n);
}
}

// if key contains multiple words, evaluate the expression with ExpressionValidator
else
{
var expression = new ExpressionValidator(key);
var result = expression.Validates(data);
var validator = new ExpressionBooleanValidator(key);
var result = validator.Validates(data);

if (!result)
{
Expand Down
8 changes: 4 additions & 4 deletions Htmt/AttributeParsers/UnlessAttributeParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Htmt.AttributeParsers;
public class UnlessAttributeParser : IAttributeParser
{
public string XTag => "//*[@x:unless]";

public void Parse(XmlDocument xml, Dictionary<string, object?> data, XmlNodeList? nodes)
{
// No nodes found
Expand All @@ -21,7 +21,7 @@ public void Parse(XmlDocument xml, Dictionary<string, object?> data, XmlNodeList

var key = n.GetAttribute("x:unless");
n.RemoveAttribute("x:unless");

// if key is a single word, we just check for a truthy value
if (!key.Contains(' '))
{
Expand All @@ -47,8 +47,8 @@ public void Parse(XmlDocument xml, Dictionary<string, object?> data, XmlNodeList
// if key contains multiple words, evaluate the expression with ExpressionValidator
else
{
var expression = new ExpressionValidator(key);
var result = expression.Validates(data);
var validator = new ExpressionBooleanValidator(key);
var result = validator.Validates(data);

if (result)
{
Expand Down
36 changes: 18 additions & 18 deletions Htmt/ExpressionValidator.cs → Htmt/ExpressionBooleanValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace Htmt;

public class ExpressionValidator(string expression)
public class ExpressionBooleanValidator(string expression)
{
public bool Validates(Dictionary<string, object?> data)
{
Expand All @@ -14,27 +14,27 @@ private string Normalize(Dictionary<string, object?> data)
{
var evaluatedExpression = expression;
var keyMatches = Regex.Matches(expression, """'(?<string>[^']*)'|(?<key>\w+)""");

foreach (Match match in keyMatches)
{
// if this is not a key, skip
if (match.Groups["key"].Success == false) continue;

var key = match.Groups["key"].Value;

// skip if the key is an operator
if (key is "is" or "and" or "or" or "not") continue;

// skip if key is integer, double, float, boolean, null or string
if (int.TryParse(key, out _)) continue;
if (double.TryParse(key, out _)) continue;
if (float.TryParse(key, out _)) continue;
if (bool.TryParse(key, out _)) continue;
if (key == "null") continue;

var value = Helper.FindValueByKeys(data, key.Split('.'));
var regex = new Regex($@"{key}");

// if value is bool, lowercase it
if (value is bool b)
{
Expand All @@ -44,11 +44,11 @@ private string Normalize(Dictionary<string, object?> data)

evaluatedExpression = regex.Replace(evaluatedExpression, value?.ToString() ?? "null", 1);
}

// remove single quotes from string values
return Regex.Replace(evaluatedExpression, "'(.*?)'", "$1");
}

private static bool EvaluateExp(string exp)
{
while (exp.Contains('('))
Expand All @@ -60,10 +60,10 @@ private static bool EvaluateExp(string exp)
{
throw new SyntaxErrorException("Invalid expression");
}

var subExp = exp.Substring(openIndex + 1, closeIndex - openIndex - 1);
var subResult = EvaluateSimpleExp(subExp);

exp = exp.Substring(0, openIndex) + subResult.ToString().ToLower() + exp.Substring(closeIndex + 1);
}

Expand All @@ -83,36 +83,36 @@ private static bool EvaluateSimpleExp(string exp)
{
andResult &= EvaluateCondition(andPart.Trim());
}

if (andResult) return true;
}

return false;
}

private static bool EvaluateCondition(string condition)
{
if (condition == "true") return true;
if (condition == "false") return false;

if (condition.Contains(" is "))
{
var parts = condition.Split([" is "], StringSplitOptions.None);
var key = parts[0].Trim();
var value = parts[1].Trim();

return key == value;
}

if (condition.Contains(" not "))
{
var parts = condition.Split([" not "], StringSplitOptions.None);
var key = parts[0].Trim();
var value = parts[1].Trim();

return key != value;
}

throw new SyntaxErrorException("Invalid condition");
}
}
38 changes: 38 additions & 0 deletions Htmt/ExpressionModifierParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System;

namespace Htmt;

public class ExpressionModifierParser
{
public Dictionary<string, object?> Data { get; init; } = [];

public IExpressionModifier[] ExpressionModifiers { get; init; } = [];

public object? Parse(string expression)
{
var parts = expression.Split('|', StringSplitOptions.TrimEntries);
var key = parts[0];
var value = Helper.FindValueByKeys(Data, key.Split('.'));

// No expression modifiers found
if (parts.Length == 1)
{
return value;
}

var modifier = parts[1];
var modifierName = modifier.Split(':', StringSplitOptions.TrimEntries)[0];
var modifierArgs = modifier.Contains(':') ? modifier.Split(':', StringSplitOptions.TrimEntries)[1] : null;

// Find the modifier instance
var modifierInstance = ExpressionModifiers.FirstOrDefault(m => m.Name == modifierName);

if (modifierInstance == null)
{
return value;
}

// Off to the races.
return modifierInstance.Modify(value, modifierArgs);
}
}
20 changes: 20 additions & 0 deletions Htmt/ExpressionModifiers/DateExpressionModifier.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;

namespace Htmt.ExpressionModifiers;

public class DateExpressionModifier : IExpressionModifier
{
public string Name => "date";

public object? Modify(object? value, string? args = null)
{
const string defaultFormat = "yyyy-MM-dd";

if (value is string s)
{
return !DateTime.TryParse(s, out var dtParsed) ? value : dtParsed.ToString(args ?? defaultFormat);
}

return value is not DateTime dt ? value : dt.ToString(args ?? defaultFormat);
}
}
Loading

0 comments on commit bf27401

Please sign in to comment.