From c59b263446e10ef6024b7dd9c947c27fe6ce2777 Mon Sep 17 00:00:00 2001 From: Kyle Fuller Date: Mon, 28 Nov 2016 18:17:01 +0000 Subject: [PATCH] feat(if): Support `>`, `>=`, `<` and `<=` operators Closes #52 --- CHANGELOG.md | 2 +- Sources/Expression.swift | 82 +++++++++++++++++++++++++ Sources/IfTag.swift | 4 ++ Tests/StencilTests/ExpressionSpec.swift | 48 +++++++++++++++ docs/builtins.rst | 44 +++++++++++++ 5 files changed, 179 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf026f8..c9598f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,7 @@ values of Swift structures and classes inside a Context. - If tags can now use prefix and infix operators such as `not`, `and`, `or`, - `==`, and `!=`. + `==`, `!=`, `>`, `>=`, `<` and `<=`. ```html+django {% if one or two and not three %} diff --git a/Sources/Expression.swift b/Sources/Expression.swift index 926b8c2..5609e82 100644 --- a/Sources/Expression.swift +++ b/Sources/Expression.swift @@ -170,6 +170,88 @@ class EqualityExpression: Expression, InfixOperator, CustomStringConvertible { } +class NumericExpression: Expression, InfixOperator, CustomStringConvertible { + let lhs: Expression + let rhs: Expression + + required init(lhs: Expression, rhs: Expression) { + self.lhs = lhs + self.rhs = rhs + } + + var description: String { + return "(\(lhs) \(op) \(rhs))" + } + + func evaluate(context: Context) throws -> Bool { + if let lhs = lhs as? VariableExpression, let rhs = rhs as? VariableExpression { + let lhsValue = try lhs.variable.resolve(context) + let rhsValue = try rhs.variable.resolve(context) + + if let lhs = lhsValue, let rhs = rhsValue { + if let lhs = toNumber(value: lhs), let rhs = toNumber(value: rhs) { + return compare(lhs: lhs, rhs: rhs) + } + } + } + + return false + } + + var op: String { + return "" + } + + func compare(lhs: Float80, rhs: Float80) -> Bool { + return false + } +} + + +class MoreThanExpression: NumericExpression { + override var op: String { + return ">" + } + + override func compare(lhs: Float80, rhs: Float80) -> Bool { + return lhs > rhs + } +} + + +class MoreThanEqualExpression: NumericExpression { + override var op: String { + return ">=" + } + + override func compare(lhs: Float80, rhs: Float80) -> Bool { + return lhs >= rhs + } +} + + +class LessThanExpression: NumericExpression { + override var op: String { + return "<" + } + + override func compare(lhs: Float80, rhs: Float80) -> Bool { + return lhs < rhs + } +} + + +class LessThanEqualExpression: NumericExpression { + override var op: String { + return "<=" + } + + override func compare(lhs: Float80, rhs: Float80) -> Bool { + return lhs <= rhs + } +} + + class InequalityExpression: EqualityExpression { override var description: String { return "(\(lhs) != \(rhs))" diff --git a/Sources/IfTag.swift b/Sources/IfTag.swift index 2658bfc..d48622e 100644 --- a/Sources/IfTag.swift +++ b/Sources/IfTag.swift @@ -19,6 +19,10 @@ let operators: [Operator] = [ .prefix("not", 8, NotExpression.self), .infix("==", 10, EqualityExpression.self), .infix("!=", 10, InequalityExpression.self), + .infix(">", 10, MoreThanExpression.self), + .infix(">=", 10, MoreThanEqualExpression.self), + .infix("<", 10, LessThanExpression.self), + .infix("<=", 10, LessThanEqualExpression.self), ] diff --git a/Tests/StencilTests/ExpressionSpec.swift b/Tests/StencilTests/ExpressionSpec.swift index feea653..e14e551 100644 --- a/Tests/StencilTests/ExpressionSpec.swift +++ b/Tests/StencilTests/ExpressionSpec.swift @@ -202,6 +202,54 @@ func testExpressions() { } } + $0.describe("more than expression") { + let expression = try! parseExpression(components: ["lhs", ">", "rhs"]) + + $0.it("evaluates to true with lhs > rhs") { + try expect(expression.evaluate(context: Context(dictionary: ["lhs": 5.0, "rhs": 4]))).to.beTrue() + } + + $0.it("evaluates to false with lhs == rhs") { + try expect(expression.evaluate(context: Context(dictionary: ["lhs": 5.0, "rhs": 5.0]))).to.beFalse() + } + } + + $0.describe("more than equal expression") { + let expression = try! parseExpression(components: ["lhs", ">=", "rhs"]) + + $0.it("evaluates to true with lhs == rhs") { + try expect(expression.evaluate(context: Context(dictionary: ["lhs": 5.0, "rhs": 5]))).to.beTrue() + } + + $0.it("evaluates to false with lhs < rhs") { + try expect(expression.evaluate(context: Context(dictionary: ["lhs": 5.0, "rhs": 5.1]))).to.beFalse() + } + } + + $0.describe("less than expression") { + let expression = try! parseExpression(components: ["lhs", "<", "rhs"]) + + $0.it("evaluates to true with lhs < rhs") { + try expect(expression.evaluate(context: Context(dictionary: ["lhs": 4, "rhs": 4.5]))).to.beTrue() + } + + $0.it("evaluates to false with lhs == rhs") { + try expect(expression.evaluate(context: Context(dictionary: ["lhs": 5.0, "rhs": 5.0]))).to.beFalse() + } + } + + $0.describe("less than equal expression") { + let expression = try! parseExpression(components: ["lhs", "<=", "rhs"]) + + $0.it("evaluates to true with lhs == rhs") { + try expect(expression.evaluate(context: Context(dictionary: ["lhs": 5.0, "rhs": 5]))).to.beTrue() + } + + $0.it("evaluates to false with lhs > rhs") { + try expect(expression.evaluate(context: Context(dictionary: ["lhs": 5.1, "rhs": 5.0]))).to.beFalse() + } + } + $0.describe("multiple expression") { let expression = try! parseExpression(components: ["one", "or", "two", "and", "not", "three"]) diff --git a/docs/builtins.rst b/docs/builtins.rst index 3d81499..dae4739 100644 --- a/docs/builtins.rst +++ b/docs/builtins.rst @@ -117,6 +117,50 @@ Will be treated as: .. note:: The inequality operator only supports numerical, string and boolean types. +``<`` operator +""""""""""""""" + +.. code-block:: html+django + + {% if value < other_value %} + value is less than other_value + {% endif %} + +.. note:: The less than operator only supports numerical types. + +``<=`` operator +""""""""""""""" + +.. code-block:: html+django + + {% if value <= other_value %} + value is less than or equal to other_value + {% endif %} + +.. note:: The less than equal operator only supports numerical types. + +``>`` operator +""""""""""""""" + +.. code-block:: html+django + + {% if value > other_value %} + value is more than other_value + {% endif %} + +.. note:: The more than operator only supports numerical types. + +``>=`` operator +""""""""""""""" + +.. code-block:: html+django + + {% if value >= other_value %} + value is more than or equal to other_value + {% endif %} + +.. note:: The more than equal operator only supports numerical types. + ``ifnot`` ~~~~~~~~~