@@ -3,6 +3,14 @@ import Foundation
33
44typealias 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
715class 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
125187func normalize( _ current: Any ? ) -> Any ? {
126188 if let current = current as? Normalizable {
0 commit comments