170 lines
3.9 KiB
Swift
170 lines
3.9 KiB
Swift
protocol Expression: CustomStringConvertible {
|
|
func evaluate(context: Context) throws -> Bool
|
|
}
|
|
|
|
|
|
protocol InfixOperator: Expression {
|
|
init(lhs: Expression, rhs: Expression)
|
|
}
|
|
|
|
|
|
protocol PrefixOperator: Expression {
|
|
init(expression: Expression)
|
|
}
|
|
|
|
|
|
final class StaticExpression: Expression, CustomStringConvertible {
|
|
let value: Bool
|
|
|
|
init(value: Bool) {
|
|
self.value = value
|
|
}
|
|
|
|
func evaluate(context: Context) throws -> Bool {
|
|
return value
|
|
}
|
|
|
|
var description: String {
|
|
return "\(value)"
|
|
}
|
|
}
|
|
|
|
|
|
final class VariableExpression: Expression, CustomStringConvertible {
|
|
let variable: Variable
|
|
|
|
init(variable: Variable) {
|
|
self.variable = variable
|
|
}
|
|
|
|
var description: String {
|
|
return "(variable: \(variable.variable))"
|
|
}
|
|
|
|
/// Resolves a variable in the given context as boolean
|
|
func resolve(context: Context, variable: Variable) throws -> Bool {
|
|
let result = try variable.resolve(context)
|
|
var truthy = false
|
|
|
|
if let result = result as? [Any] {
|
|
truthy = !result.isEmpty
|
|
} else if let result = result as? [String:Any] {
|
|
truthy = !result.isEmpty
|
|
} else if let result = result as? Bool {
|
|
truthy = result
|
|
} else if let value = result, let result = toNumber(value: value) {
|
|
truthy = result > 0
|
|
} else if result != nil {
|
|
truthy = true
|
|
}
|
|
|
|
return truthy
|
|
}
|
|
|
|
func evaluate(context: Context) throws -> Bool {
|
|
return try resolve(context: context, variable: variable)
|
|
}
|
|
}
|
|
|
|
|
|
final class NotExpression: Expression, PrefixOperator, CustomStringConvertible {
|
|
let expression: Expression
|
|
|
|
init(expression: Expression) {
|
|
self.expression = expression
|
|
}
|
|
|
|
var description: String {
|
|
return "not \(expression)"
|
|
}
|
|
|
|
func evaluate(context: Context) throws -> Bool {
|
|
return try !expression.evaluate(context: context)
|
|
}
|
|
}
|
|
|
|
|
|
final class OrExpression: 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) or \(rhs))"
|
|
}
|
|
|
|
func evaluate(context: Context) throws -> Bool {
|
|
let lhs = try self.lhs.evaluate(context: context)
|
|
if lhs {
|
|
return lhs
|
|
}
|
|
|
|
return try rhs.evaluate(context: context)
|
|
}
|
|
}
|
|
|
|
|
|
final class AndExpression: 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) and \(rhs))"
|
|
}
|
|
|
|
func evaluate(context: Context) throws -> Bool {
|
|
let lhs = try self.lhs.evaluate(context: context)
|
|
if !lhs {
|
|
return lhs
|
|
}
|
|
|
|
return try rhs.evaluate(context: context)
|
|
}
|
|
}
|
|
|
|
|
|
func toNumber(value: Any) -> Float80? {
|
|
if let value = value as? Float {
|
|
return Float80(value)
|
|
} else if let value = value as? Double {
|
|
return Float80(value)
|
|
} else if let value = value as? UInt {
|
|
return Float80(value)
|
|
} else if let value = value as? Int {
|
|
return Float80(value)
|
|
} else if let value = value as? Int8 {
|
|
return Float80(value)
|
|
} else if let value = value as? Int16 {
|
|
return Float80(value)
|
|
} else if let value = value as? Int32 {
|
|
return Float80(value)
|
|
} else if let value = value as? Int64 {
|
|
return Float80(value)
|
|
} else if let value = value as? UInt8 {
|
|
return Float80(value)
|
|
} else if let value = value as? UInt16 {
|
|
return Float80(value)
|
|
} else if let value = value as? UInt32 {
|
|
return Float80(value)
|
|
} else if let value = value as? UInt64 {
|
|
return Float80(value)
|
|
} else if let value = value as? Float80 {
|
|
return value
|
|
} else if let value = value as? Float64 {
|
|
return Float80(value)
|
|
} else if let value = value as? Float32 {
|
|
return Float80(value)
|
|
}
|
|
|
|
return nil
|
|
}
|