Skip to content

Commit 052fba2

Browse files
committed
Basic numeric expresion handling
1 parent 39517b7 commit 052fba2

1 file changed

Lines changed: 67 additions & 5 deletions

File tree

Sources/Variable.swift

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@ import Foundation
33

44
typealias Number = Float
55

6+
public protocol GenericVariable: Equatable {
7+
var variable: String { get }
8+
}
9+
10+
public func ==<T: GenericVariable>(lhs: T, rhs: T) -> Bool {
11+
return lhs.variable == rhs.variable
12+
}
13+
614

715
class FilterExpression : Resolvable {
816
let filters: [(FilterType, [Variable])]
@@ -41,8 +49,66 @@ class FilterExpression : Resolvable {
4149
}
4250
}
4351

52+
// A structure used to represent a template variable or expression, resolved in
53+
// a given context
54+
public struct CompoundVariable : GenericVariable, Resolvable {
55+
public let variable: String
56+
57+
/// Create a variable with a string representing the variable
58+
public init(_ variable: String) {
59+
self.variable = variable
60+
}
61+
62+
/// Resolve the variable in the given context, first as a normal variable, then
63+
/// as an expression (more expensive)
64+
public func resolve(_ context: Context) throws -> Any? {
65+
var result = try Variable(variable).resolve(context)
66+
67+
if result == nil {
68+
result = try expressionResolve(context)
69+
}
70+
71+
return result
72+
}
73+
74+
private func expressionResolve(_ context: Context) throws -> Any? {
75+
var components = explode(expression: variable, operators: " +-*/()")
76+
77+
// try to resolve each individual component
78+
components = try components.map {
79+
if let resolved = try Variable($0).resolve(context) {
80+
return stringify(resolved)
81+
} else {
82+
return $0
83+
}
84+
}
85+
86+
let expression = NSExpression(format: components.joined())
87+
return expression.expressionValue(with: nil, context: nil)
88+
}
89+
90+
private func explode(expression: String, operators: String) -> [String] {
91+
let set = CharacterSet(charactersIn: operators)
92+
var result = [String]()
93+
94+
var current = ""
95+
for character in expression.unicodeScalars {
96+
if !set.contains(character) {
97+
current += String(character)
98+
} else {
99+
result.append(current)
100+
result.append(String(character))
101+
current = ""
102+
}
103+
}
104+
result.append(current)
105+
106+
return result.filter { !$0.isEmpty }
107+
}
108+
}
109+
44110
/// A structure used to represent a template variable, and to resolve it in a given context.
45-
public struct Variable : Equatable, Resolvable {
111+
public struct Variable : GenericVariable, Resolvable {
46112
public let variable: String
47113

48114
/// Create a variable with a string representing the variable
@@ -117,10 +183,6 @@ public struct Variable : Equatable, Resolvable {
117183
}
118184
}
119185

120-
public func ==(lhs: Variable, rhs: Variable) -> Bool {
121-
return lhs.variable == rhs.variable
122-
}
123-
124186

125187
func normalize(_ current: Any?) -> Any? {
126188
if let current = current as? Normalizable {

0 commit comments

Comments
 (0)