From 3180b266737fe278a6944b1863e08bc042c9406f Mon Sep 17 00:00:00 2001 From: Ilya Puchka Date: Sun, 1 Oct 2017 12:46:48 +0200 Subject: [PATCH] feat: added in expression in if tag (#143) --- CHANGELOG.md | 1 + Sources/Expression.swift | 31 +++++++++++++++++++++++++ Sources/IfTag.swift | 1 + Tests/StencilTests/ExpressionSpec.swift | 16 +++++++++++++ 4 files changed, 49 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b14aaa6..2494ee2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ the for loop 0 indexed. - Introduces a new `DictionaryLoader` for loading templates from a Swift Dictionary. +- Added `in` expression in if tag for strings and arrays of hashable types ### Bug Fixes diff --git a/Sources/Expression.swift b/Sources/Expression.swift index f2c8a48..1f41afe 100644 --- a/Sources/Expression.swift +++ b/Sources/Expression.swift @@ -85,6 +85,37 @@ final class NotExpression: Expression, PrefixOperator, CustomStringConvertible { } } +final class InExpression: 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) in \(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 as? AnyHashable, let rhs = rhsValue as? [AnyHashable] { + return rhs.contains(lhs) + } else if let lhs = lhsValue as? String, let rhs = rhsValue as? String { + return rhs.contains(lhs) + } else if lhsValue == nil && rhsValue == nil { + return true + } + } + + return false + } + +} final class OrExpression: Expression, InfixOperator, CustomStringConvertible { let lhs: Expression diff --git a/Sources/IfTag.swift b/Sources/IfTag.swift index 16c926d..8f3b0fd 100644 --- a/Sources/IfTag.swift +++ b/Sources/IfTag.swift @@ -14,6 +14,7 @@ enum Operator { let operators: [Operator] = [ + .infix("in", 5, InExpression.self), .infix("or", 6, OrExpression.self), .infix("and", 7, AndExpression.self), .prefix("not", 8, NotExpression.self), diff --git a/Tests/StencilTests/ExpressionSpec.swift b/Tests/StencilTests/ExpressionSpec.swift index a7b7241..4b7958d 100644 --- a/Tests/StencilTests/ExpressionSpec.swift +++ b/Tests/StencilTests/ExpressionSpec.swift @@ -279,6 +279,22 @@ func testExpressions() { try expect(expression.evaluate(context: Context())).to.beFalse() } } + + $0.describe("in expression") { + let expression = try! parseExpression(components: ["lhs", "in", "rhs"], tokenParser: parser) + + $0.it("evaluates to true when rhs contains lhs") { + try expect(expression.evaluate(context: Context(dictionary: ["lhs": 1, "rhs": [1, 2, 3]]))).to.beTrue() + try expect(expression.evaluate(context: Context(dictionary: ["lhs": "a", "rhs": ["a", "b", "c"]]))).to.beTrue() + try expect(expression.evaluate(context: Context(dictionary: ["lhs": "a", "rhs": "abc"]))).to.beTrue() + } + + $0.it("evaluates to false when rhs does not contain lhs") { + try expect(expression.evaluate(context: Context(dictionary: ["lhs": 1, "rhs": [2, 3, 4]]))).to.beFalse() + try expect(expression.evaluate(context: Context(dictionary: ["lhs": "a", "rhs": ["b", "c", "d"]]))).to.beFalse() + try expect(expression.evaluate(context: Context(dictionary: ["lhs": "a", "rhs": "bcd"]))).to.beFalse() + } + } } } }