diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e578a1..167de4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,8 @@ - Variable lookup now supports introspection of Swift types. You can now lookup values of Swift structures and classes inside a Context. -- If tags can now use prefix and infix operators such as `not`, `and` and `or`. +- If tags can now use prefix and infix operators such as `not`, `and`, `or` and + `==`. ```html+django {% if one or two and not three %} diff --git a/Sources/Expression.swift b/Sources/Expression.swift index f063033..b935556 100644 --- a/Sources/Expression.swift +++ b/Sources/Expression.swift @@ -134,6 +134,42 @@ final class AndExpression: Expression, InfixOperator, CustomStringConvertible { } +final class EqualityExpression: Expression, InfixOperator, CustomStringConvertible { + let lhs: Expression + let rhs: Expression + + init(lhs: Expression, rhs: Expression) { + self.lhs = lhs + self.rhs = rhs + } + + var description: String { + return "(\(lhs) == \(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 lhs == rhs + } else if let lhs = lhsValue as? String, let rhs = rhsValue as? String { + return lhs == rhs + } else if let lhs = lhsValue as? Bool, let rhs = rhsValue as? Bool { + return lhs == rhs + } + } else if lhsValue == nil && rhsValue == nil { + return true + } + } + + return false + } +} + + func toNumber(value: Any) -> Float80? { if let value = value as? Float { return Float80(value) diff --git a/Sources/IfTag.swift b/Sources/IfTag.swift index b8ef3f7..68c117d 100644 --- a/Sources/IfTag.swift +++ b/Sources/IfTag.swift @@ -17,6 +17,7 @@ let operators: [Operator] = [ .infix("or", 6, OrExpression.self), .infix("and", 7, AndExpression.self), .prefix("not", 8, NotExpression.self), + .infix("==", 10, EqualityExpression.self), ] diff --git a/Tests/StencilTests/ExpressionSpec.swift b/Tests/StencilTests/ExpressionSpec.swift index 6dbb44e..d0e7bc3 100644 --- a/Tests/StencilTests/ExpressionSpec.swift +++ b/Tests/StencilTests/ExpressionSpec.swift @@ -154,6 +154,42 @@ func testExpressions() { } } + $0.describe("equality expression") { + let expression = try! parseExpression(components: ["lhs", "==", "rhs"]) + + $0.it("evaluates to true with equal lhs/rhs") { + try expect(expression.evaluate(context: Context(dictionary: ["lhs": "a", "rhs": "a"]))).to.beTrue() + } + + $0.it("evaluates to false with non equal lhs/rhs") { + print(expression) + } + + $0.it("evaluates to true with nils") { + try expect(expression.evaluate(context: Context(dictionary: [:]))).to.beTrue() + } + + $0.it("evaluates to true with numbers") { + try expect(expression.evaluate(context: Context(dictionary: ["lhs": 1, "rhs": 1.0]))).to.beTrue() + } + + $0.it("evaluates to false with non equal numbers") { + try expect(expression.evaluate(context: Context(dictionary: ["lhs": 1, "rhs": 1.1]))).to.beFalse() + } + + $0.it("evaluates to true with booleans") { + try expect(expression.evaluate(context: Context(dictionary: ["lhs": true, "rhs": true]))).to.beTrue() + } + + $0.it("evaluates to false with falsy booleans") { + try expect(expression.evaluate(context: Context(dictionary: ["lhs": true, "rhs": false]))).to.beFalse() + } + + $0.it("evaluates to false with different types") { + try expect(expression.evaluate(context: Context(dictionary: ["lhs": true, "rhs": 1]))).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 34899b4..0053bf5 100644 --- a/docs/builtins.rst +++ b/docs/builtins.rst @@ -95,6 +95,17 @@ Will be treated as: one or (two and three) +``==`` operator +""""""""""""""" + +.. code-block:: html+django + + {% if value == other_value %} + value is equal to other_value + {% endif %} + +.. note:: The equality operator only supports numerical, string and boolean types. + ``ifnot`` ~~~~~~~~~