Files
swiftpm-stencil/Sources/Expression.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
}