Files
swiftpm-stencil/Sources/Expression.swift
2016-11-28 19:24:26 +00:00

301 lines
6.7 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 result = result as? String {
truthy = !result.isEmpty
} 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)
}
}
class EqualityExpression: Expression, InfixOperator, CustomStringConvertible {
let lhs: Expression
let rhs: Expression
required init(lhs: Expression, rhs: Expression) {
self.lhs = lhs
self.rhs = rhs
}
var description: String {
return "(\(lhs) == \(rhs))"
}
func evaluate(context: Context) throws -> Bool {
if let lhs = lhs as? VariableExpression, let rhs = rhs as? VariableExpression {
let lhsValue = try lhs.variable.resolve(context)
let rhsValue = try rhs.variable.resolve(context)
if let lhs = lhsValue, let rhs = rhsValue {
if let lhs = toNumber(value: lhs), let rhs = toNumber(value: rhs) {
return lhs == rhs
} else if let lhs = lhsValue as? String, let rhs = rhsValue as? String {
return lhs == rhs
} else if let lhs = lhsValue as? Bool, let rhs = rhsValue as? Bool {
return lhs == rhs
}
} else if lhsValue == nil && rhsValue == nil {
return true
}
}
return false
}
}
class NumericExpression: Expression, InfixOperator, CustomStringConvertible {
let lhs: Expression
let rhs: Expression
required init(lhs: Expression, rhs: Expression) {
self.lhs = lhs
self.rhs = rhs
}
var description: String {
return "(\(lhs) \(op) \(rhs))"
}
func evaluate(context: Context) throws -> Bool {
if let lhs = lhs as? VariableExpression, let rhs = rhs as? VariableExpression {
let lhsValue = try lhs.variable.resolve(context)
let rhsValue = try rhs.variable.resolve(context)
if let lhs = lhsValue, let rhs = rhsValue {
if let lhs = toNumber(value: lhs), let rhs = toNumber(value: rhs) {
return compare(lhs: lhs, rhs: rhs)
}
}
}
return false
}
var op: String {
return ""
}
func compare(lhs: Float80, rhs: Float80) -> Bool {
return false
}
}
class MoreThanExpression: NumericExpression {
override var op: String {
return ">"
}
override func compare(lhs: Float80, rhs: Float80) -> Bool {
return lhs > rhs
}
}
class MoreThanEqualExpression: NumericExpression {
override var op: String {
return ">="
}
override func compare(lhs: Float80, rhs: Float80) -> Bool {
return lhs >= rhs
}
}
class LessThanExpression: NumericExpression {
override var op: String {
return "<"
}
override func compare(lhs: Float80, rhs: Float80) -> Bool {
return lhs < rhs
}
}
class LessThanEqualExpression: NumericExpression {
override var op: String {
return "<="
}
override func compare(lhs: Float80, rhs: Float80) -> Bool {
return lhs <= rhs
}
}
class InequalityExpression: EqualityExpression {
override var description: String {
return "(\(lhs) != \(rhs))"
}
override func evaluate(context: Context) throws -> Bool {
return try !super.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
}