From ab5c3a48a8091e4bf7954428ecfc958683b41ed9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asko=20N=C3=B5mm?= Date: Thu, 17 Oct 2024 16:49:50 +0300 Subject: [PATCH 1/5] Implement expression validator. --- Htmt/AttributeParsers/ForAttributeParser.cs | 4 +- .../GenericValueAttributeParser.cs | 2 +- Htmt/AttributeParsers/IfAttributeParser.cs | 57 +++++--- .../InnerHtmlAttributeParser.cs | 2 +- .../InnerTextAttributeParser.cs | 2 +- .../OuterHtmlAttributeParser.cs | 2 +- .../OuterTextAttributeParser.cs | 2 +- .../AttributeParsers/UnlessAttributeParser.cs | 47 ++++--- Htmt/ExpressionValidator.cs | 125 +++++++++++++++++ Htmt/Helper.cs | 4 +- Htmt/IAttributeParser.cs | 2 +- Htmt/Parser.cs | 4 +- HtmtTests/AttributeParserTest.cs | 24 +++- HtmtTests/ExpressionValidatorTest.cs | 127 ++++++++++++++++++ HtmtTests/HelperTest.cs | 6 +- 15 files changed, 359 insertions(+), 51 deletions(-) create mode 100644 Htmt/ExpressionValidator.cs create mode 100644 HtmtTests/ExpressionValidatorTest.cs diff --git a/Htmt/AttributeParsers/ForAttributeParser.cs b/Htmt/AttributeParsers/ForAttributeParser.cs index bb40950..ddf37ff 100644 --- a/Htmt/AttributeParsers/ForAttributeParser.cs +++ b/Htmt/AttributeParsers/ForAttributeParser.cs @@ -6,7 +6,7 @@ public class ForAttributeParser : IAttributeParser { public string XTag => "//*[@x:for]"; - public void Parse(XmlDocument xml, Dictionary data, XmlNodeList? nodes) + public void Parse(XmlDocument xml, Dictionary data, XmlNodeList? nodes) { // No nodes found if (nodes == null || nodes.Count == 0) @@ -31,7 +31,7 @@ public void Parse(XmlDocument xml, Dictionary data, XmlNodeList? foreach (var item in enumerable) { - var iterationData = new Dictionary(data); + var iterationData = new Dictionary(data); if (!string.IsNullOrEmpty(asVar)) { diff --git a/Htmt/AttributeParsers/GenericValueAttributeParser.cs b/Htmt/AttributeParsers/GenericValueAttributeParser.cs index de0d88e..2419939 100644 --- a/Htmt/AttributeParsers/GenericValueAttributeParser.cs +++ b/Htmt/AttributeParsers/GenericValueAttributeParser.cs @@ -6,7 +6,7 @@ public class GenericValueAttributeParser : IAttributeParser { public string XTag => "//*[@*[starts-with(name(), 'x:')]]"; - public void Parse(XmlDocument xml, Dictionary data, XmlNodeList? nodes) + public void Parse(XmlDocument xml, Dictionary data, XmlNodeList? nodes) { // No nodes found if (nodes == null || nodes.Count == 0) diff --git a/Htmt/AttributeParsers/IfAttributeParser.cs b/Htmt/AttributeParsers/IfAttributeParser.cs index 4d7654e..df8ec00 100644 --- a/Htmt/AttributeParsers/IfAttributeParser.cs +++ b/Htmt/AttributeParsers/IfAttributeParser.cs @@ -7,7 +7,7 @@ public class IfAttributeParser : IAttributeParser { public string XTag => "//*[@x:if]"; - public void Parse(XmlDocument xml, Dictionary data, XmlNodeList? nodes) + public void Parse(XmlDocument xml, Dictionary data, XmlNodeList? nodes) { // No nodes found if (nodes == null || nodes.Count == 0) @@ -21,30 +21,47 @@ public void Parse(XmlDocument xml, Dictionary data, XmlNodeList? var key = n.GetAttribute("x:if"); n.RemoveAttribute("x:if"); - var value = Helper.FindValueByKeys(data, key.Split('.')); - // Remove node if value is null - if(value == null) + // if key is a single word, we just check for a truthy value + if (!key.Contains(' ')) { - n.ParentNode?.RemoveChild(n); - continue; + var value = Helper.FindValueByKeys(data, key.Split('.')); + + // Remove node if value is null + if (value == null) + { + n.ParentNode?.RemoveChild(n); + continue; + } + + // Remove node if value is falsey + var removeNode = value switch + { + bool b => !b, + int i => i == 0, + double d => d == 0, + string s => string.IsNullOrEmpty(s), + IEnumerable e => !e.Any(), + IDictionary d => d.Count == 0, + _ => false + }; + + if (removeNode) + { + n.ParentNode?.RemoveChild(n); + } } - // Remove node if value is falsey - var removeNode = value switch + // if key contains multiple words, evaluate the expression with ExpressionValidator + else { - bool b => !b, - int i => i == 0, - double d => d == 0, - string s => string.IsNullOrEmpty(s), - IEnumerable e => !e.Any(), - IDictionary d => d.Count == 0, - _ => false - }; - - if (removeNode) - { - n.ParentNode?.RemoveChild(n); + var expression = new ExpressionValidator(key); + var result = expression.Validates(data); + + if (!result) + { + n.ParentNode?.RemoveChild(n); + } } } } diff --git a/Htmt/AttributeParsers/InnerHtmlAttributeParser.cs b/Htmt/AttributeParsers/InnerHtmlAttributeParser.cs index d0eeab6..2dbeb38 100644 --- a/Htmt/AttributeParsers/InnerHtmlAttributeParser.cs +++ b/Htmt/AttributeParsers/InnerHtmlAttributeParser.cs @@ -7,7 +7,7 @@ public class InnerHtmlAttributeParser: IAttributeParser { public string XTag => "//*[@x:inner-html]"; - public void Parse(XmlDocument xml, Dictionary data, XmlNodeList? nodes) + public void Parse(XmlDocument xml, Dictionary data, XmlNodeList? nodes) { // No nodes found if (nodes == null || nodes.Count == 0) diff --git a/Htmt/AttributeParsers/InnerTextAttributeParser.cs b/Htmt/AttributeParsers/InnerTextAttributeParser.cs index 147fbe7..bfc6ac3 100644 --- a/Htmt/AttributeParsers/InnerTextAttributeParser.cs +++ b/Htmt/AttributeParsers/InnerTextAttributeParser.cs @@ -7,7 +7,7 @@ public class InnerTextAttributeParser : IAttributeParser { public string XTag => "//*[@x:inner-text]"; - public void Parse(XmlDocument xml, Dictionary data, XmlNodeList? nodes) + public void Parse(XmlDocument xml, Dictionary data, XmlNodeList? nodes) { // No nodes found if (nodes == null || nodes.Count == 0) diff --git a/Htmt/AttributeParsers/OuterHtmlAttributeParser.cs b/Htmt/AttributeParsers/OuterHtmlAttributeParser.cs index 4581e3f..850ffe6 100644 --- a/Htmt/AttributeParsers/OuterHtmlAttributeParser.cs +++ b/Htmt/AttributeParsers/OuterHtmlAttributeParser.cs @@ -6,7 +6,7 @@ public class OuterHtmlAttributeParser : IAttributeParser { public string XTag => "//*[@x:outer-html]"; - public void Parse(XmlDocument xml, Dictionary data, XmlNodeList? nodes) + public void Parse(XmlDocument xml, Dictionary data, XmlNodeList? nodes) { // No nodes found if (nodes == null || nodes.Count == 0) diff --git a/Htmt/AttributeParsers/OuterTextAttributeParser.cs b/Htmt/AttributeParsers/OuterTextAttributeParser.cs index c0db626..0c8ec81 100644 --- a/Htmt/AttributeParsers/OuterTextAttributeParser.cs +++ b/Htmt/AttributeParsers/OuterTextAttributeParser.cs @@ -7,7 +7,7 @@ public partial class OuterTextAttributeParser : IAttributeParser { public string XTag => "//*[@x:outer-text]"; - public void Parse(XmlDocument xml, Dictionary data, XmlNodeList? nodes) + public void Parse(XmlDocument xml, Dictionary data, XmlNodeList? nodes) { // No nodes found if (nodes == null || nodes.Count == 0) diff --git a/Htmt/AttributeParsers/UnlessAttributeParser.cs b/Htmt/AttributeParsers/UnlessAttributeParser.cs index 13abfed..b53f0f6 100644 --- a/Htmt/AttributeParsers/UnlessAttributeParser.cs +++ b/Htmt/AttributeParsers/UnlessAttributeParser.cs @@ -7,7 +7,7 @@ public class UnlessAttributeParser : IAttributeParser { public string XTag => "//*[@x:unless]"; - public void Parse(XmlDocument xml, Dictionary data, XmlNodeList? nodes) + public void Parse(XmlDocument xml, Dictionary data, XmlNodeList? nodes) { // No nodes found if (nodes == null || nodes.Count == 0) @@ -21,22 +21,39 @@ public void Parse(XmlDocument xml, Dictionary data, XmlNodeList? var key = n.GetAttribute("x:unless"); n.RemoveAttribute("x:unless"); - var value = Helper.FindValueByKeys(data, key.Split('.')); - - var removeNode = value switch + + // if key is a single word, we just check for a truthy value + if (!key.Contains(' ')) { - bool b => b, - int i => i != 0, - double d => d != 0, - string s => !string.IsNullOrEmpty(s), - IEnumerable e => e.Any(), - IDictionary d => d.Count > 0, - _ => true - }; - - if (removeNode) + var value = Helper.FindValueByKeys(data, key.Split('.')); + + var removeNode = value switch + { + bool b => b, + int i => i != 0, + double d => d != 0, + string s => !string.IsNullOrEmpty(s), + IEnumerable e => e.Any(), + IDictionary d => d.Count > 0, + _ => true + }; + + if (removeNode) + { + n.ParentNode?.RemoveChild(n); + } + } + + // if key contains multiple words, evaluate the expression with ExpressionValidator + else { - n.ParentNode?.RemoveChild(n); + var expression = new ExpressionValidator(key); + var result = expression.Validates(data); + + if (result) + { + n.ParentNode?.RemoveChild(n); + } } } } diff --git a/Htmt/ExpressionValidator.cs b/Htmt/ExpressionValidator.cs new file mode 100644 index 0000000..bbbebef --- /dev/null +++ b/Htmt/ExpressionValidator.cs @@ -0,0 +1,125 @@ +using System.Data; +using System.Text.RegularExpressions; + +namespace Htmt; + +public class ExpressionValidator(string expression) +{ + public bool Validates(Dictionary data) + { + return EvaluateExp(Normalize(data)); + } + + private string Normalize(Dictionary data) + { + var evaluatedExpression = expression; + var keyMatches = Regex.Matches(expression, """'(?[^']*)'|(?\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) + { + evaluatedExpression = regex.Replace(evaluatedExpression, b.ToString().ToLower(), 1); + continue; + } + + 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('(')) + { + var openIndex = exp.LastIndexOf('('); + var closeIndex = exp.IndexOf(')', openIndex); + + if (openIndex == -1 || closeIndex == -1) + { + 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); + } + + return EvaluateSimpleExp(exp); + } + + private static bool EvaluateSimpleExp(string exp) + { + var conditionParts = exp.Split(new[] { " is " }, StringSplitOptions.None); + + if (conditionParts.Length > 1) + { + return EvaluateCondition(exp.Trim()); + } + + var orParts = exp.Split(new[] { " or " }, StringSplitOptions.None); + + foreach (var orPart in orParts) + { + var andParts = orPart.Split([" and "], StringSplitOptions.None); + var andResult = true; + + foreach (var andPart in andParts) + { + 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"); + } +} \ No newline at end of file diff --git a/Htmt/Helper.cs b/Htmt/Helper.cs index 9e8a9ce..166a234 100644 --- a/Htmt/Helper.cs +++ b/Htmt/Helper.cs @@ -6,7 +6,7 @@ namespace Htmt; public partial class Helper { - public static object? FindValueByKeys(Dictionary data, string[] keys) + public static object? FindValueByKeys(Dictionary data, string[] keys) { while (true) { @@ -64,7 +64,7 @@ public partial class Helper [GeneratedRegex(@"(?\{.*?\})")] private static partial Regex WholeKeyRegex(); - public static string ReplaceKeysWithData(string str, Dictionary data) + public static string ReplaceKeysWithData(string str, Dictionary data) { var matches = WholeKeyRegex().Matches(str).Select(x => x.Groups["name"].Value).ToArray(); diff --git a/Htmt/IAttributeParser.cs b/Htmt/IAttributeParser.cs index 47cade1..400ce1a 100644 --- a/Htmt/IAttributeParser.cs +++ b/Htmt/IAttributeParser.cs @@ -6,5 +6,5 @@ public interface IAttributeParser { public string XTag { get; } - public void Parse(XmlDocument xml, Dictionary data, XmlNodeList? nodes); + public void Parse(XmlDocument xml, Dictionary data, XmlNodeList? nodes); } \ No newline at end of file diff --git a/Htmt/Parser.cs b/Htmt/Parser.cs index 00df9fa..ff31218 100644 --- a/Htmt/Parser.cs +++ b/Htmt/Parser.cs @@ -10,7 +10,7 @@ public class Parser public string Template { get; init; } = string.Empty; - public Dictionary Data { get; init; } = new(); + public Dictionary Data { get; init; } = new(); public IAttributeParser[] AttributeParsers { get; init; } = []; @@ -120,7 +120,7 @@ private void RunAttributeParsers() foreach(var parser in parsers) { var nodes = Xml.DocumentElement?.SelectNodes(parser.XTag, _nsManager); - var clonedData = new Dictionary(Data); + var clonedData = new Dictionary(Data); parser.Parse(Xml, clonedData, nodes); } diff --git a/HtmtTests/AttributeParserTest.cs b/HtmtTests/AttributeParserTest.cs index ebec669..24f63a4 100644 --- a/HtmtTests/AttributeParserTest.cs +++ b/HtmtTests/AttributeParserTest.cs @@ -160,13 +160,24 @@ public void TestIfDictionaryAttributeParser() public void TestIfEmptyDictionaryAttributeParser() { const string template = "

There are items!

"; - var data = new Dictionary { { "items", new Dictionary() } }; + var data = new Dictionary { { "items", new Dictionary() } }; var parser = new Parser { Template = template, Data = data }; var html = parser.ToHtml(); Assert.AreEqual("", html); } + [TestMethod] + public void TestComplexExpressionIfAttributeParser() + { + const string template = "

"; + var data = new Dictionary { { "show", true }, { "title", "Hello, World!" } }; + var parser = new Parser { Template = template, Data = data }; + var html = parser.ToHtml(); + + Assert.AreEqual("

Hello, World!

", html); + } + [TestMethod] public void TestUnlessAttributeParser() { @@ -260,6 +271,17 @@ public void TestUnlessEmptyDictionaryAttributeParser() Assert.AreEqual("

There are no items!

", html); } + + [TestMethod] + public void TestComplexExpressionUnlessAttributeParser() + { + const string template = "

"; + var data = new Dictionary { { "show", true }, { "title", "Hello, World!" } }; + var parser = new Parser { Template = template, Data = data }; + var html = parser.ToHtml(); + + Assert.AreEqual("", html); + } [TestMethod] public void TestForAttributeParser() diff --git a/HtmtTests/ExpressionValidatorTest.cs b/HtmtTests/ExpressionValidatorTest.cs new file mode 100644 index 0000000..973edf9 --- /dev/null +++ b/HtmtTests/ExpressionValidatorTest.cs @@ -0,0 +1,127 @@ +using Htmt; + +namespace HtmtTests; + +[TestClass] +public class ExpressionValidatorTest +{ + [TestMethod] + public void TestSimpleOr() + { + var returnsTrue = new ExpressionValidator("(1 is 1) or (2 is 3)").Validates(new Dictionary()); + var returnsFalse = new ExpressionValidator("(1 is 2) or (2 is 3)").Validates(new Dictionary()); + + Assert.IsTrue(returnsTrue); + Assert.IsFalse(returnsFalse); + } + + [TestMethod] + public void TestIntComparisons() + { + var returnsTrue = new ExpressionValidator("test is 1").Validates(new Dictionary() { { "test", 1 } }); + var returnsFalse = new ExpressionValidator("test is 1").Validates(new Dictionary() { { "test", 2 } }); + + Assert.IsTrue(returnsTrue); + Assert.IsFalse(returnsFalse); + } + + [TestMethod] + public void TestDoubleComparisons() + { + var returnsTrue = new ExpressionValidator("test is 1.1").Validates(new Dictionary() { { "test", 1.1 } }); + var returnsFalse = new ExpressionValidator("test is 1.1").Validates(new Dictionary() { { "test", 1.2 } }); + + Assert.IsTrue(returnsTrue); + Assert.IsFalse(returnsFalse); + } + + [TestMethod] + public void TestFloatComparisons() + { + var returnsTrue = new ExpressionValidator("test is 1.1").Validates(new Dictionary() { { "test", 1.1f } }); + var returnsFalse = new ExpressionValidator("test is 1.1").Validates(new Dictionary() { { "test", 1.2f } }); + + Assert.IsTrue(returnsTrue); + Assert.IsFalse(returnsFalse); + } + + [TestMethod] + public void TestStringComparisons() + { + var returnsTrue = new ExpressionValidator("test is 'test'").Validates(new Dictionary() { { "test", "test" } }); + var returnsFalse = new ExpressionValidator("test is 'test'").Validates(new Dictionary() { { "test", "test2" } }); + + Assert.IsTrue(returnsTrue); + Assert.IsFalse(returnsFalse); + } + + [TestMethod] + public void TestBooleanComparisons() + { + var returnsTrue = new ExpressionValidator("test is true").Validates(new Dictionary() { { "test", true } }); + var returnsFalse = new ExpressionValidator("test is true").Validates(new Dictionary() { { "test", false } }); + + Assert.IsTrue(returnsTrue); + Assert.IsFalse(returnsFalse); + } + + [TestMethod] + public void TestNullComparisons() + { + var returnsTrue = new ExpressionValidator("test is null").Validates(new Dictionary() { { "test", null } }); + var returnsFalse = new ExpressionValidator("test is null").Validates(new Dictionary() { { "test", "test" } }); + + Assert.IsTrue(returnsTrue); + Assert.IsFalse(returnsFalse); + } + + [TestMethod] + public void TestAnd() + { + var returnsTrue = new ExpressionValidator("(test is 1) and (test2 is 2)").Validates(new Dictionary() { { "test", 1 }, { "test2", 2 } }); + var returnsFalse = new ExpressionValidator("(test is 1) and (test2 is 2)").Validates(new Dictionary() { { "test", 1 }, { "test2", 3 } }); + + Assert.IsTrue(returnsTrue); + Assert.IsFalse(returnsFalse); + } + + [TestMethod] + public void TestComplexAndExpression() + { + var returnsTrue = new ExpressionValidator("((test is 1) and (test2 is 2)) or (test3 is 3)").Validates(new Dictionary() { { "test", 1 }, { "test2", 2 }, { "test3", 3 } }); + var returnsFalse = new ExpressionValidator("((test is 1) and (test2 is 2))").Validates(new Dictionary() { { "test", 1 }, { "test2", 3 } }); + + Assert.IsTrue(returnsTrue); + Assert.IsFalse(returnsFalse); + } + + [TestMethod] + public void TestComplexOrExpression() + { + var returnsTrue = new ExpressionValidator("((test is 1) or (test2 is 2)) and (test3 is 3)").Validates(new Dictionary() { { "test", 1 }, { "test2", 3 }, { "test3", 3 } }); + var returnsFalse = new ExpressionValidator("((test is 1) or (test2 is 2))").Validates(new Dictionary() { { "test", 3 }, { "test2", 3 } }); + + Assert.IsTrue(returnsTrue); + Assert.IsFalse(returnsFalse); + } + + [TestMethod] + public void TestComplexExpression() + { + var returnsTrue = new ExpressionValidator("((test is 1) or (test2 is 2)) and (test3 is 3)").Validates(new Dictionary() { { "test", 1 }, { "test2", 3 }, { "test3", 3 } }); + var returnsFalse = new ExpressionValidator("((test is 1) or (test2 is 2))").Validates(new Dictionary() { { "test", 3 }, { "test2", 3 } }); + + Assert.IsTrue(returnsTrue); + Assert.IsFalse(returnsFalse); + } + + [TestMethod] + public void TestMultipleStringComparisons() + { + var returnsTrue = new ExpressionValidator("(test is 'test') and (test2 is 'test2')").Validates(new Dictionary() { { "test", "test" }, { "test2", "test2" } }); + var returnsFalse = new ExpressionValidator("(test is 'test') and (test2 is 'test2')").Validates(new Dictionary() { { "test", "test" }, { "test2", "test3" } }); + + Assert.IsTrue(returnsTrue); + Assert.IsFalse(returnsFalse); + } +} \ No newline at end of file diff --git a/HtmtTests/HelperTest.cs b/HtmtTests/HelperTest.cs index 75afc43..ca61412 100644 --- a/HtmtTests/HelperTest.cs +++ b/HtmtTests/HelperTest.cs @@ -8,7 +8,7 @@ public class HelperTest [TestMethod] public void TestReplaceKeysWithData() { - var data = new Dictionary { { "name", "John Doe" } }; + var data = new Dictionary { { "name", "John Doe" } }; var result = Helper.ReplaceKeysWithData("Hello, {name}!", data); Assert.AreEqual("Hello, John Doe!", result); @@ -17,7 +17,7 @@ public void TestReplaceKeysWithData() [TestMethod] public void TestReplaceKeysWithDataWithMultipleKeys() { - var data = new Dictionary { { "name", "John Doe" }, { "age", 30 } }; + var data = new Dictionary { { "name", "John Doe" }, { "age", 30 } }; var result = Helper.ReplaceKeysWithData("Hello, {name}! You are {age} years old.", data); Assert.AreEqual("Hello, John Doe! You are 30 years old.", result); @@ -26,7 +26,7 @@ public void TestReplaceKeysWithDataWithMultipleKeys() [TestMethod] public void TestReplaceKeysWithDataWithEmptyData() { - var result = Helper.ReplaceKeysWithData("Hello, {name}!", new Dictionary()); + var result = Helper.ReplaceKeysWithData("Hello, {name}!", new Dictionary()); Assert.AreEqual("Hello, !", result); } From 85d458ffc120770faeb519cd894289d13e37c707 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asko=20N=C3=B5mm?= Date: Thu, 17 Oct 2024 16:54:25 +0300 Subject: [PATCH 2/5] I think this is not needed at all. --- Htmt/ExpressionValidator.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Htmt/ExpressionValidator.cs b/Htmt/ExpressionValidator.cs index bbbebef..743adc2 100644 --- a/Htmt/ExpressionValidator.cs +++ b/Htmt/ExpressionValidator.cs @@ -72,13 +72,6 @@ private static bool EvaluateExp(string exp) private static bool EvaluateSimpleExp(string exp) { - var conditionParts = exp.Split(new[] { " is " }, StringSplitOptions.None); - - if (conditionParts.Length > 1) - { - return EvaluateCondition(exp.Trim()); - } - var orParts = exp.Split(new[] { " or " }, StringSplitOptions.None); foreach (var orPart in orParts) From 751fa8dc4c92263ce328d5824fea54ca3a68efaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asko=20N=C3=B5mm?= Date: Fri, 18 Oct 2024 00:54:07 +0300 Subject: [PATCH 3/5] Improvements --- Htmt/Helper.cs | 14 +++++++------- HtmtTests/ExpressionValidatorTest.cs | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Htmt/Helper.cs b/Htmt/Helper.cs index 166a234..6f9ce77 100644 --- a/Htmt/Helper.cs +++ b/Htmt/Helper.cs @@ -30,27 +30,27 @@ public partial class Helper keys = newKeys; continue; } - case Dictionary dict: + case Dictionary dict: { var newKeys = keys.Skip(1).ToArray(); - data = dict.ToDictionary(x => x.Key, x => (object)x.Value); + data = dict.ToDictionary(x => x.Key, object? (x) => x.Value); keys = newKeys; continue; } - case Dictionary dict: + case Dictionary dict: { var newKeys = keys.Skip(1).ToArray(); - data = dict.ToDictionary(x => x.Key, x => (object)x.Value); + data = dict.ToDictionary(x => x.Key, x => (object?) x.Value); keys = newKeys; continue; } - case Dictionary dict: + case Dictionary dict: { var newKeys = keys.Skip(1).ToArray(); - - data = dict.ToDictionary(x => x.Key, x => (object)x.Value); + + data = dict.ToDictionary(x => x.Key, x => (object?) x.Value); keys = newKeys; continue; } diff --git a/HtmtTests/ExpressionValidatorTest.cs b/HtmtTests/ExpressionValidatorTest.cs index 973edf9..e65d157 100644 --- a/HtmtTests/ExpressionValidatorTest.cs +++ b/HtmtTests/ExpressionValidatorTest.cs @@ -48,7 +48,7 @@ public void TestFloatComparisons() [TestMethod] public void TestStringComparisons() { - var returnsTrue = new ExpressionValidator("test is 'test'").Validates(new Dictionary() { { "test", "test" } }); + var returnsTrue = new ExpressionValidator("test is 'test'").Validates(new Dictionary() { { "test", "test" } }); var returnsFalse = new ExpressionValidator("test is 'test'").Validates(new Dictionary() { { "test", "test2" } }); Assert.IsTrue(returnsTrue); From 105d218c1d1d78bfd926ff163fd788f6a6025118 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asko=20N=C3=B5mm?= Date: Fri, 18 Oct 2024 01:45:04 +0300 Subject: [PATCH 4/5] More improvements --- HtmtTests/AttributeParserTest.cs | 41 +++++++++++++++----------------- HtmtTests/ParserTest.cs | 6 ++--- 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/HtmtTests/AttributeParserTest.cs b/HtmtTests/AttributeParserTest.cs index 24f63a4..c3bf8fd 100644 --- a/HtmtTests/AttributeParserTest.cs +++ b/HtmtTests/AttributeParserTest.cs @@ -9,8 +9,7 @@ public class AttributeParserTest public void TestGenericValueAttributeParser() { const string template = "Click here"; - var data = new Dictionary - { { "url", "https://www.example.com" }, { "name", "Example Website" } }; + var data = new Dictionary { { "url", "https://www.example.com" }, { "name", "Example Website" } }; var parser = new Parser { Template = template, Data = data }; var html = parser.ToHtml(); @@ -33,7 +32,7 @@ public void TestGenericValueAttributeParserWithouData() public void TestInnerTextAttributeParser() { const string template = "

"; - var data = new Dictionary { { "title", "Hello, World!" } }; + var data = new Dictionary { { "title", "Hello, World!" } }; var parser = new Parser { Template = template, Data = data }; var html = parser.ToHtml(); @@ -44,7 +43,7 @@ public void TestInnerTextAttributeParser() public void TestInnerHtmlAttributeParser() { const string template = "
"; - var data = new Dictionary { { "content", "

Hello, World!

" } }; + var data = new Dictionary { { "content", "

Hello, World!

" } }; var parser = new Parser { Template = template, Data = data }; var html = parser.ToHtml(); @@ -55,7 +54,7 @@ public void TestInnerHtmlAttributeParser() public void TestOuterTextAttributeParser() { const string template = "

"; - var data = new Dictionary { { "title", "Hello, World!" } }; + var data = new Dictionary { { "title", "Hello, World!" } }; var parser = new Parser { Template = template, Data = data }; var html = parser.ToHtml(); @@ -66,7 +65,7 @@ public void TestOuterTextAttributeParser() public void TestOuterHtmlAttributeParser() { const string template = "
"; - var data = new Dictionary { { "content", "

Hello, World!

" } }; + var data = new Dictionary { { "content", "

Hello, World!

" } }; var parser = new Parser { Template = template, Data = data }; var html = parser.ToHtml(); @@ -77,7 +76,7 @@ public void TestOuterHtmlAttributeParser() public void TestIfAttributeParser() { const string template = "

"; - var data = new Dictionary { { "show", true }, { "title", "Hello, World!" } }; + var data = new Dictionary { { "show", true }, { "title", "Hello, World!" } }; var parser = new Parser { Template = template, Data = data }; var html = parser.ToHtml(); @@ -88,7 +87,7 @@ public void TestIfAttributeParser() public void TestIfNotAttributeParser() { const string template = "

"; - var data = new Dictionary { { "show", false }, { "title", "Hello, World!" } }; + var data = new Dictionary { { "show", false }, { "title", "Hello, World!" } }; var parser = new Parser { Template = template, Data = data }; var html = parser.ToHtml(); @@ -109,7 +108,7 @@ public void TestIfNoDataAttributeParser() public void TestIfListAttributeParser() { const string template = "

There are items!

"; - var data = new Dictionary { { "items", new List { "Item 1", "Item 2", "Item 3" } } }; + var data = new Dictionary { { "items", new List { "Item 1", "Item 2", "Item 3" } } }; var parser = new Parser { Template = template, Data = data }; var html = parser.ToHtml(); @@ -120,7 +119,7 @@ public void TestIfListAttributeParser() public void TestIfListOfDictsAttributeParser() { const string template = "

There are items!

"; - var data = new Dictionary + var data = new Dictionary { { "items", @@ -137,7 +136,7 @@ public void TestIfListOfDictsAttributeParser() public void TestIfEmptyListAttributeParser() { const string template = "

There are items!

"; - var data = new Dictionary { { "items", new List() } }; + var data = new Dictionary { { "items", new List() } }; var parser = new Parser { Template = template, Data = data }; var html = parser.ToHtml(); @@ -148,8 +147,7 @@ public void TestIfEmptyListAttributeParser() public void TestIfDictionaryAttributeParser() { const string template = "

There are items!

"; - var data = new Dictionary - { { "items", new Dictionary { { "key1", "Item 1" }, { "key2", "Item 2" } } } }; + var data = new Dictionary { { "items", new Dictionary { { "key1", "Item 1" }, { "key2", "Item 2" } } } }; var parser = new Parser { Template = template, Data = data }; var html = parser.ToHtml(); @@ -182,7 +180,7 @@ public void TestComplexExpressionIfAttributeParser() public void TestUnlessAttributeParser() { const string template = "

"; - var data = new Dictionary { { "show", false }, { "title", "Hello, World!" } }; + var data = new Dictionary { { "show", false }, { "title", "Hello, World!" } }; var parser = new Parser { Template = template, Data = data }; var html = parser.ToHtml(); @@ -193,7 +191,7 @@ public void TestUnlessAttributeParser() public void TestUnlessNotAttributeParser() { const string template = "

"; - var data = new Dictionary { { "show", true }, { "title", "Hello, World!" } }; + var data = new Dictionary { { "show", true }, { "title", "Hello, World!" } }; var parser = new Parser { Template = template, Data = data }; var html = parser.ToHtml(); @@ -214,7 +212,7 @@ public void TestUnlessNoDataAttributeParser() public void TestUnlessListAttributeParser() { const string template = "

There are no items!

"; - var data = new Dictionary { { "items", new List { "Item 1", "Item 2", "Item 3" } } }; + var data = new Dictionary { { "items", new List { "Item 1", "Item 2", "Item 3" } } }; var parser = new Parser { Template = template, Data = data }; var html = parser.ToHtml(); @@ -225,7 +223,7 @@ public void TestUnlessListAttributeParser() public void TestUnlessListOfDictsAttributeParser() { const string template = "

There are no items!

"; - var data = new Dictionary + var data = new Dictionary { { "items", @@ -242,7 +240,7 @@ public void TestUnlessListOfDictsAttributeParser() public void TestUnlessEmptyListAttributeParser() { const string template = "

There are no items!

"; - var data = new Dictionary { { "items", new List() } }; + var data = new Dictionary { { "items", new List() } }; var parser = new Parser { Template = template, Data = data }; var html = parser.ToHtml(); @@ -253,8 +251,7 @@ public void TestUnlessEmptyListAttributeParser() public void TestUnlessDictionaryAttributeParser() { const string template = "

There are no items!

"; - var data = new Dictionary - { { "items", new Dictionary { { "key1", "Item 1" }, { "key2", "Item 2" } } } }; + var data = new Dictionary { { "items", new Dictionary { { "key1", "Item 1" }, { "key2", "Item 2" } } } }; var parser = new Parser { Template = template, Data = data }; var html = parser.ToHtml(); @@ -265,7 +262,7 @@ public void TestUnlessDictionaryAttributeParser() public void TestUnlessEmptyDictionaryAttributeParser() { const string template = "

There are no items!

"; - var data = new Dictionary { { "items", new Dictionary() } }; + var data = new Dictionary { { "items", new Dictionary() } }; var parser = new Parser { Template = template, Data = data }; var html = parser.ToHtml(); @@ -288,7 +285,7 @@ public void TestForAttributeParser() { const string template = "
"; - var data = new Dictionary { { "items", new List { "Item 1", "Item 2", "Item 3" } } }; + var data = new Dictionary { { "items", new List { "Item 1", "Item 2", "Item 3" } } }; var parser = new Parser { Template = template, Data = data }; var html = parser.ToHtml(); diff --git a/HtmtTests/ParserTest.cs b/HtmtTests/ParserTest.cs index 0908df6..010b0aa 100644 --- a/HtmtTests/ParserTest.cs +++ b/HtmtTests/ParserTest.cs @@ -9,7 +9,7 @@ public class ParserTest public void TestHtml5Document() { const string template = "

"; - var data = new Dictionary { { "title", "Hello, World!" }, { "heading", "Welcome to the world!" } }; + var data = new Dictionary { { "title", "Hello, World!" }, { "heading", "Welcome to the world!" } }; var parser = new Htmt.Parser { Template = template, Data = data }; var html = parser.ToHtml(); @@ -20,7 +20,7 @@ public void TestHtml5Document() public void TestHtml5DocumentWithComments() { const string template = "

"; - var data = new Dictionary { { "title", "Hello, World!" }, { "heading", "Welcome to the world!" } }; + var data = new Dictionary { { "title", "Hello, World!" }, { "heading", "Welcome to the world!" } }; var parser = new Htmt.Parser { Template = template, Data = data }; var html = parser.ToHtml(); @@ -31,7 +31,7 @@ public void TestHtml5DocumentWithComments() public void TestHtml4Document() { const string template = "

"; - var data = new Dictionary { { "title", "Hello, World!" }, { "heading", "Welcome to the world!" } }; + var data = new Dictionary { { "title", "Hello, World!" }, { "heading", "Welcome to the world!" } }; var parser = new Htmt.Parser { Template = template, Data = data }; var html = parser.ToHtml(); From 16dcc67469dce84e49ed07e2ffb5a48ecd2ba11e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asko=20N=C3=B5mm?= Date: Fri, 18 Oct 2024 01:55:40 +0300 Subject: [PATCH 5/5] And even more --- Htmt/Helper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Htmt/Helper.cs b/Htmt/Helper.cs index 6f9ce77..479b6c3 100644 --- a/Htmt/Helper.cs +++ b/Htmt/Helper.cs @@ -22,7 +22,7 @@ public partial class Helper switch (v) { - case Dictionary dict: + case Dictionary dict: { var newKeys = keys.Skip(1).ToArray();