@@ -12,42 +12,39 @@ class Environment {
12
12
13
13
var variables : [ String : any RuntimeValue ] = [
14
14
" namespace " : FunctionValue ( value: { args, _ in
15
- if args. count == 0 {
15
+ if args. isEmpty {
16
16
return ObjectValue ( value: [ : ] )
17
17
}
18
18
19
- if args. count != 1 || ! ( args [ 0 ] is ObjectValue ) {
19
+ guard args. count == 1 , let objectArg = args [ 0 ] as? ObjectValue else {
20
20
throw JinjaError . runtime ( " `namespace` expects either zero arguments or a single object argument " )
21
21
}
22
22
23
- return args [ 0 ]
23
+ return objectArg
24
24
} )
25
25
]
26
26
27
27
var tests : [ String : ( any RuntimeValue . . . ) throws -> Bool ] = [
28
- " boolean " : {
29
- args in
30
- args [ 0 ] is BooleanValue
28
+ " boolean " : { args in
29
+ return args [ 0 ] is BooleanValue
31
30
} ,
32
31
33
- " callable " : {
34
- args in
35
- args [ 0 ] is FunctionValue
32
+ " callable " : { args in
33
+ return args [ 0 ] is FunctionValue
36
34
} ,
37
35
38
- " odd " : {
39
- args in
40
- if let arg = args. first as? NumericValue {
41
- return arg. value as! Int % 2 != 0
36
+ " odd " : { args in
37
+ if let arg = args. first as? NumericValue , let intValue = arg. value as? Int {
38
+ return intValue % 2 != 0
42
39
} else {
43
- throw JinjaError . runtime ( " Cannot apply test 'odd' to type: \( type ( of: args. first) ) " )
40
+ throw JinjaError . runtime ( " Cannot apply test 'odd' to type: \( type ( of: args. first) ) " )
44
41
}
45
42
} ,
46
43
" even " : { args in
47
- if let arg = args. first as? NumericValue {
48
- return arg . value as! Int % 2 == 0
44
+ if let arg = args. first as? NumericValue , let intValue = arg . value as? Int {
45
+ return intValue % 2 == 0
49
46
} else {
50
- throw JinjaError . runtime ( " Cannot apply test 'even' to type: \( type ( of: args. first) ) " )
47
+ throw JinjaError . runtime ( " Cannot apply test 'even' to type: \( type ( of: args. first) ) " )
51
48
}
52
49
} ,
53
50
" false " : { args in
@@ -62,24 +59,25 @@ class Environment {
62
59
}
63
60
return false
64
61
} ,
62
+ " string " : { args in
63
+ return args [ 0 ] is StringValue
64
+ } ,
65
65
" number " : { args in
66
- args [ 0 ] is NumericValue
66
+ return args [ 0 ] is NumericValue
67
67
} ,
68
68
" integer " : { args in
69
69
if let arg = args [ 0 ] as? NumericValue {
70
70
return arg. value is Int
71
71
}
72
-
73
72
return false
74
73
} ,
75
74
" iterable " : { args in
76
- args [ 0 ] is ArrayValue || args [ 0 ] is StringValue
75
+ return args [ 0 ] is ArrayValue || args [ 0 ] is StringValue
77
76
} ,
78
77
" lower " : { args in
79
78
if let arg = args [ 0 ] as? StringValue {
80
79
return arg. value == arg. value. lowercased ( )
81
80
}
82
-
83
81
return false
84
82
} ,
85
83
" upper " : { args in
@@ -89,16 +87,47 @@ class Environment {
89
87
return false
90
88
} ,
91
89
" none " : { args in
92
- args [ 0 ] is NullValue
90
+ return args [ 0 ] is NullValue
93
91
} ,
94
92
" defined " : { args in
95
- !( args [ 0 ] is UndefinedValue )
93
+ return !( args [ 0 ] is UndefinedValue )
96
94
} ,
97
95
" undefined " : { args in
98
- args [ 0 ] is UndefinedValue
96
+ return args [ 0 ] is UndefinedValue
99
97
} ,
100
- " equalto " : { _ in
101
- throw JinjaError . syntaxNotSupported ( " equalto " )
98
+ " equalto " : { args in
99
+ if args. count == 2 {
100
+ if let left = args [ 0 ] as? StringValue , let right = args [ 1 ] as? StringValue {
101
+ return left. value == right. value
102
+ } else if let left = args [ 0 ] as? NumericValue , let right = args [ 1 ] as? NumericValue ,
103
+ let leftInt = left. value as? Int , let rightInt = right. value as? Int
104
+ {
105
+ return leftInt == rightInt
106
+ } else if let left = args [ 0 ] as? BooleanValue , let right = args [ 1 ] as? BooleanValue {
107
+ return left. value == right. value
108
+ } else {
109
+ return false
110
+ }
111
+ } else {
112
+ return false
113
+ }
114
+ } ,
115
+ " eq " : { args in
116
+ if args. count == 2 {
117
+ if let left = args [ 0 ] as? StringValue , let right = args [ 1 ] as? StringValue {
118
+ return left. value == right. value
119
+ } else if let left = args [ 0 ] as? NumericValue , let right = args [ 1 ] as? NumericValue ,
120
+ let leftInt = left. value as? Int , let rightInt = right. value as? Int
121
+ {
122
+ return leftInt == rightInt
123
+ } else if let left = args [ 0 ] as? BooleanValue , let right = args [ 1 ] as? BooleanValue {
124
+ return left. value == right. value
125
+ } else {
126
+ return false
127
+ }
128
+ } else {
129
+ return false
130
+ }
102
131
} ,
103
132
]
104
133
@@ -107,61 +136,53 @@ class Environment {
107
136
}
108
137
109
138
func isFunction< T> ( _ value: Any , functionType: T . Type ) -> Bool {
110
- value is T
139
+ return value is T
111
140
}
112
141
113
142
func convertToRuntimeValues( input: Any ) throws -> any RuntimeValue {
114
143
switch input {
115
144
case let value as Bool :
116
145
return BooleanValue ( value: value)
117
- case let values as [ any Numeric ] :
118
- var items : [ any RuntimeValue ] = [ ]
119
- for value in values {
120
- try items. append ( self . convertToRuntimeValues ( input: value) )
121
- }
122
- return ArrayValue ( value: items)
123
146
case let value as any Numeric :
124
147
return NumericValue ( value: value)
125
148
case let value as String :
126
149
return StringValue ( value: value)
127
150
case let fn as ( String ) throws -> Void :
128
151
return FunctionValue { args, _ in
129
- var arg = " "
130
- switch args [ 0 ] . value {
131
- case let value as String :
132
- arg = value
133
- case let value as Bool :
134
- arg = String ( value)
135
- default :
136
- throw JinjaError . runtime ( " Unknown arg type: \( type ( of: args [ 0 ] . value) ) " )
152
+ guard let stringArg = args [ 0 ] as? StringValue else {
153
+ throw JinjaError . runtime ( " Argument must be a StringValue " )
137
154
}
138
-
139
- try fn ( arg)
155
+ try fn ( stringArg. value)
140
156
return NullValue ( )
141
157
}
142
158
case let fn as ( Bool ) throws -> Void :
143
159
return FunctionValue { args, _ in
144
- try fn ( args [ 0 ] . value as! Bool )
160
+ guard let boolArg = args [ 0 ] as? BooleanValue else {
161
+ throw JinjaError . runtime ( " Argument must be a BooleanValue " )
162
+ }
163
+ try fn ( boolArg. value)
145
164
return NullValue ( )
146
165
}
147
166
case let fn as ( Int , Int ? , Int ) -> [ Int ] :
148
167
return FunctionValue { args, _ in
149
- let result = fn ( args [ 0 ] . value as! Int , args [ 1 ] . value as? Int , args [ 2 ] . value as! Int )
168
+ guard let arg0 = args [ 0 ] as? NumericValue , let int0 = arg0. value as? Int else {
169
+ throw JinjaError . runtime ( " First argument must be an Int " )
170
+ }
171
+ let int1 = ( args [ 1 ] as? NumericValue ) ? . value as? Int
172
+ guard let arg2 = args [ 2 ] as? NumericValue , let int2 = arg2. value as? Int else {
173
+ throw JinjaError . runtime ( " Third argument must be an Int " )
174
+ }
175
+ let result = fn ( int0, int1, int2)
150
176
return try self . convertToRuntimeValues ( input: result)
151
177
}
152
178
case let values as [ Any ] :
153
- var items : [ any RuntimeValue ] = [ ]
154
- for value in values {
155
- try items. append ( self . convertToRuntimeValues ( input: value) )
156
- }
179
+ let items = try values. map { try self . convertToRuntimeValues ( input: $0) }
157
180
return ArrayValue ( value: items)
158
- case let dictionary as [ String : String ] :
181
+ case let dictionary as [ String : Any ] :
159
182
var object : [ String : any RuntimeValue ] = [ : ]
160
-
161
183
for (key, value) in dictionary {
162
- object [ key] = StringValue ( value : value)
184
+ object [ key] = try self . convertToRuntimeValues ( input : value)
163
185
}
164
-
165
186
return ObjectValue ( value: object)
166
187
case is NullValue :
167
188
return NullValue ( )
@@ -176,12 +197,11 @@ class Environment {
176
197
}
177
198
178
199
func declareVariable( name: String , value: any RuntimeValue ) throws -> any RuntimeValue {
179
- if self . variables. contains ( where : { $0 . 0 == name } ) {
200
+ if self . variables. keys . contains ( name) {
180
201
throw JinjaError . syntax ( " Variable already declared: \( name) " )
181
202
}
182
203
183
204
self . variables [ name] = value
184
-
185
205
return value
186
206
}
187
207
@@ -191,25 +211,21 @@ class Environment {
191
211
return value
192
212
}
193
213
194
- func resolve( name: String ) throws -> Self {
195
- if self . variables. contains ( where : { $0 . 0 == name } ) {
214
+ func resolve( name: String ) throws -> Environment {
215
+ if self . variables. keys . contains ( name) {
196
216
return self
197
217
}
198
218
199
- if let parent {
200
- return try parent. resolve ( name: name) as! Self
219
+ if let parent = self . parent {
220
+ return try parent. resolve ( name: name)
201
221
}
202
222
203
223
throw JinjaError . runtime ( " Unknown variable: \( name) " )
204
224
}
205
225
206
226
func lookupVariable( name: String ) -> any RuntimeValue {
207
227
do {
208
- if let value = try self . resolve ( name: name) . variables [ name] {
209
- return value
210
- } else {
211
- return UndefinedValue ( )
212
- }
228
+ return try self . resolve ( name: name) . variables [ name] ?? UndefinedValue ( )
213
229
} catch {
214
230
return UndefinedValue ( )
215
231
}
0 commit comments