Skip to content

Commit beecbd8

Browse files
committed
basic arithmetic expressions
1 parent 2e80f70 commit beecbd8

File tree

5 files changed

+262
-106
lines changed

5 files changed

+262
-106
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
- Added support for resolving superclass properties for not-NSObject subclasses
88
- The `{% for %}` tag can now iterate over tuples, structures and classes via
99
their stored properties.
10+
- Added basic aritmetic operatiosn +, -, /, *
1011

1112
### Bug Fixes
1213

Sources/Expression.swift

Lines changed: 136 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
protocol Expression: CustomStringConvertible {
2-
func evaluate(context: Context) throws -> Bool
2+
func evaluate(context: Context) throws -> Any
33
}
44

55

@@ -20,7 +20,7 @@ final class StaticExpression: Expression, CustomStringConvertible {
2020
self.value = value
2121
}
2222

23-
func evaluate(context: Context) throws -> Bool {
23+
func evaluate(context: Context) throws -> Any {
2424
return value
2525
}
2626

@@ -29,8 +29,7 @@ final class StaticExpression: Expression, CustomStringConvertible {
2929
}
3030
}
3131

32-
33-
final class VariableExpression: Expression, CustomStringConvertible {
32+
class VariableExpression: Expression, CustomStringConvertible {
3433
let variable: Resolvable
3534

3635
init(variable: Resolvable) {
@@ -42,7 +41,7 @@ final class VariableExpression: Expression, CustomStringConvertible {
4241
}
4342

4443
/// Resolves a variable in the given context as boolean
45-
func resolve(context: Context, variable: Resolvable) throws -> Bool {
44+
func evaluate(context: Context) throws -> Any {
4645
let result = try variable.resolve(context)
4746
var truthy = false
4847

@@ -62,13 +61,8 @@ final class VariableExpression: Expression, CustomStringConvertible {
6261

6362
return truthy
6463
}
65-
66-
func evaluate(context: Context) throws -> Bool {
67-
return try resolve(context: context, variable: variable)
68-
}
6964
}
7065

71-
7266
final class NotExpression: Expression, PrefixOperator, CustomStringConvertible {
7367
let expression: Expression
7468

@@ -80,8 +74,8 @@ final class NotExpression: Expression, PrefixOperator, CustomStringConvertible {
8074
return "not \(expression)"
8175
}
8276

83-
func evaluate(context: Context) throws -> Bool {
84-
return try !expression.evaluate(context: context)
77+
func evaluate(context: Context) throws -> Any {
78+
return try !(expression.evaluate(context: context) as! Bool)
8579
}
8680
}
8781

@@ -98,7 +92,7 @@ final class InExpression: Expression, InfixOperator, CustomStringConvertible {
9892
return "(\(lhs) in \(rhs))"
9993
}
10094

101-
func evaluate(context: Context) throws -> Bool {
95+
func evaluate(context: Context) throws -> Any {
10296
if let lhs = lhs as? VariableExpression, let rhs = rhs as? VariableExpression {
10397
let lhsValue = try lhs.variable.resolve(context)
10498
let rhsValue = try rhs.variable.resolve(context)
@@ -130,8 +124,8 @@ final class OrExpression: Expression, InfixOperator, CustomStringConvertible {
130124
return "(\(lhs) or \(rhs))"
131125
}
132126

133-
func evaluate(context: Context) throws -> Bool {
134-
let lhs = try self.lhs.evaluate(context: context)
127+
func evaluate(context: Context) throws -> Any {
128+
let lhs = try self.lhs.evaluate(context: context) as! Bool
135129
if lhs {
136130
return lhs
137131
}
@@ -154,8 +148,8 @@ final class AndExpression: Expression, InfixOperator, CustomStringConvertible {
154148
return "(\(lhs) and \(rhs))"
155149
}
156150

157-
func evaluate(context: Context) throws -> Bool {
158-
let lhs = try self.lhs.evaluate(context: context)
151+
func evaluate(context: Context) throws -> Any {
152+
let lhs = try self.lhs.evaluate(context: context) as! Bool
159153
if !lhs {
160154
return lhs
161155
}
@@ -178,7 +172,7 @@ class EqualityExpression: Expression, InfixOperator, CustomStringConvertible {
178172
return "(\(lhs) == \(rhs))"
179173
}
180174

181-
func evaluate(context: Context) throws -> Bool {
175+
func evaluate(context: Context) throws -> Any {
182176
if let lhs = lhs as? VariableExpression, let rhs = rhs as? VariableExpression {
183177
let lhsValue = try lhs.variable.resolve(context)
184178
let rhsValue = try rhs.variable.resolve(context)
@@ -214,16 +208,26 @@ class NumericExpression: Expression, InfixOperator, CustomStringConvertible {
214208
return "(\(lhs) \(op) \(rhs))"
215209
}
216210

217-
func evaluate(context: Context) throws -> Bool {
211+
func evaluate(context: Context) throws -> Any {
218212
if let lhs = lhs as? VariableExpression, let rhs = rhs as? VariableExpression {
219213
let lhsValue = try lhs.variable.resolve(context)
220214
let rhsValue = try rhs.variable.resolve(context)
221215

222-
if let lhs = lhsValue, let rhs = rhsValue {
223-
if let lhs = toNumber(value: lhs), let rhs = toNumber(value: rhs) {
224-
return compare(lhs: lhs, rhs: rhs)
225-
}
216+
guard let lhs = lhsValue else {
217+
throw TemplateSyntaxError("left value is nil")
218+
}
219+
guard let lhsNumber = toNumber(value: lhs) else {
220+
throw TemplateSyntaxError("left value is not a number")
221+
}
222+
223+
guard let rhs = rhsValue else {
224+
throw TemplateSyntaxError("right value is nil")
225+
}
226+
guard let rhsNumber = toNumber(value: rhs) else {
227+
throw TemplateSyntaxError("right value is not a number")
226228
}
229+
230+
return compare(lhs: lhsNumber, rhs: rhsNumber)
227231
}
228232

229233
return false
@@ -239,7 +243,7 @@ class NumericExpression: Expression, InfixOperator, CustomStringConvertible {
239243
}
240244

241245

242-
class MoreThanExpression: NumericExpression {
246+
final class MoreThanExpression: NumericExpression {
243247
override var op: String {
244248
return ">"
245249
}
@@ -250,7 +254,7 @@ class MoreThanExpression: NumericExpression {
250254
}
251255

252256

253-
class MoreThanEqualExpression: NumericExpression {
257+
final class MoreThanEqualExpression: NumericExpression {
254258
override var op: String {
255259
return ">="
256260
}
@@ -261,7 +265,7 @@ class MoreThanEqualExpression: NumericExpression {
261265
}
262266

263267

264-
class LessThanExpression: NumericExpression {
268+
final class LessThanExpression: NumericExpression {
265269
override var op: String {
266270
return "<"
267271
}
@@ -272,7 +276,7 @@ class LessThanExpression: NumericExpression {
272276
}
273277

274278

275-
class LessThanEqualExpression: NumericExpression {
279+
final class LessThanEqualExpression: NumericExpression {
276280
override var op: String {
277281
return "<="
278282
}
@@ -283,13 +287,13 @@ class LessThanEqualExpression: NumericExpression {
283287
}
284288

285289

286-
class InequalityExpression: EqualityExpression {
290+
final class InequalityExpression: EqualityExpression {
287291
override var description: String {
288292
return "(\(lhs) != \(rhs))"
289293
}
290294

291-
override func evaluate(context: Context) throws -> Bool {
292-
return try !super.evaluate(context: context)
295+
override func evaluate(context: Context) throws -> Any {
296+
return try !(super.evaluate(context: context) as! Bool)
293297
}
294298
}
295299

@@ -329,3 +333,105 @@ func toNumber(value: Any) -> Number? {
329333

330334
return nil
331335
}
336+
337+
class ArithmeticExpression: Expression, InfixOperator, CustomStringConvertible {
338+
let lhs: Expression
339+
let rhs: Expression
340+
341+
required init(lhs: Expression, rhs: Expression) {
342+
self.lhs = lhs
343+
self.rhs = rhs
344+
}
345+
346+
var description: String {
347+
return "(\(lhs) \(op) \(rhs))"
348+
}
349+
350+
func evaluate(context: Context) throws -> Any {
351+
let lhsResult: Number
352+
if let lhs = lhs as? ArithmeticExpression {
353+
lhsResult = try lhs.evaluate(context: context) as! Number
354+
} else if let lhs = lhs as? VariableExpression {
355+
let lhsValue = try lhs.variable.resolve(context)
356+
357+
guard let lhs = lhsValue else {
358+
throw TemplateSyntaxError("left value is nil")
359+
}
360+
guard let lhsNumber = toNumber(value: lhs) else {
361+
throw TemplateSyntaxError("left value '\(lhs)' is not a number")
362+
}
363+
lhsResult = lhsNumber
364+
} else {
365+
throw TemplateSyntaxError("invalid arithmetic expression")
366+
}
367+
368+
let rhsResult: Number
369+
if let rhs = rhs as? ArithmeticExpression {
370+
rhsResult = try rhs.evaluate(context: context) as! Number
371+
} else if let rhs = rhs as? VariableExpression {
372+
let rhsValue = try rhs.variable.resolve(context)
373+
374+
guard let rhs = rhsValue else {
375+
throw TemplateSyntaxError("right value is nil")
376+
}
377+
guard let rhsNumber = toNumber(value: rhs) else {
378+
throw TemplateSyntaxError("right value '\(rhs)' is not a number")
379+
}
380+
rhsResult = rhsNumber
381+
} else {
382+
throw TemplateSyntaxError("invalid arithmetic expression")
383+
}
384+
385+
return calculate(lhs: lhsResult, rhs: rhsResult)
386+
}
387+
388+
var op: String {
389+
return ""
390+
}
391+
392+
func calculate(lhs: Number, rhs: Number) -> Number {
393+
return 0
394+
}
395+
}
396+
397+
final class SumExpression: ArithmeticExpression {
398+
override var op: String {
399+
return "+"
400+
}
401+
402+
override func calculate(lhs: Number, rhs: Number) -> Number {
403+
return lhs + rhs
404+
}
405+
}
406+
407+
final class SubstractExpression: ArithmeticExpression {
408+
override var op: String {
409+
return "-"
410+
}
411+
412+
override func calculate(lhs: Number, rhs: Number) -> Number {
413+
return lhs - rhs
414+
}
415+
}
416+
417+
final class MultiplyExpression: ArithmeticExpression {
418+
override var op: String {
419+
return "*"
420+
}
421+
422+
override func calculate(lhs: Number, rhs: Number) -> Number {
423+
return lhs * rhs
424+
}
425+
}
426+
427+
final class DevideExpression: ArithmeticExpression {
428+
override var op: String {
429+
return "/"
430+
}
431+
432+
override func calculate(lhs: Number, rhs: Number) -> Number {
433+
return lhs / rhs
434+
}
435+
}
436+
437+

Sources/ForTag.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ class ForNode : NodeType {
117117
if let `where` = self.where {
118118
values = try values.filter({ item -> Bool in
119119
return try push(value: item, context: context) {
120-
try `where`.evaluate(context: context)
120+
try `where`.evaluate(context: context) as! Bool
121121
}
122122
})
123123
}

0 commit comments

Comments
 (0)