An interpreter translate our plain language expression into information and find out what we need. Here we have a Unit Interpretor (Conversion Context) who does very diverse interpretation jobs. <!–more–>

enum UnitType: String {
    case Gallon = "Gallon"
    case Pint = "Pint"
    case Cup = "Cup"
    case USD = "USD"
    case RMB = "RMB"
    case EURO = "EURO"
    case Meter = "Meter"
    case Unkonwn = "Unknown"
    init(str: String) {
        switch str {
            case "Gallon", "Gallons", "gallons": self = .Gallon
            case "Pint", "Pints", "pints": self = .Pint
            case "Cup", "Cups", "cups": self = .Cup
            case "USD", "usd", "Usd": self = .USD
            case "EURO", "euro", "Euro": self = .EURO
            case "RMB", "rmb", "Rmb": self = .RMB
        default: self = .Unkonwn
        }
    }
}

protocol Expression {
    func getUnit(quantity: Double, targetUnit: String) -> Double
}
class Gallon: NSObject, Expression {
    func getUnit(quantity: Double, targetUnit: String) -> Double {
        let targetUnitType = UnitType(str: targetUnit)
        switch targetUnitType {
        case .Gallon: return quantity
        case .Pint: return quantity * 8
        case .Cup: return quantity * 16
        default:
            print("conversion not supported")
            return -1 // just saying it's a wrong number
        }
    }
}
class Pint: NSObject, Expression {
    func getUnit(quantity: Double, targetUnit: String) -> Double {
        let targetUnitType = UnitType(str: targetUnit)
        switch targetUnitType {
        case .Gallon: return quantity * 0.125
        case .Pint: return quantity
        case .Cup: return quantity * 2
        default:
            print("conversion not supported")
            return -1
        }
        
    }
}
class USD: NSObject, Expression {
    func getUnit(quantity: Double, targetUnit: String) -> Double {
        let targetUnitType = UnitType(str: targetUnit)
        switch targetUnitType {
        case .USD: return quantity
        case .EURO: return quantity * 0.92
        case .RMB: return quantity * 6.21
        default:
            print("conversion not supported")
            return -1
        }
        
    }
}

class RMB: NSObject, Expression {
    func getUnit(quantity: Double, targetUnit: String) -> Double {

        let targetUnitType = UnitType(str: targetUnit)
        switch targetUnitType {
        case .USD: return quantity * 0.16
        case .EURO: return quantity * 0.15
        case .RMB: return quantity
        default:
            print("conversion not supported")
            return -1
        }
    }
}

struct AppConfig {
    static let AppName = "interpretor"
}
// The context to translate a sentence into related command and get the result.
class ConversionContext {
    static let sharedInstance = ConversionContext()
    private func getTargetQuantity(fromConversion: String, toConversion: String, quantity: Double) -> Double {
        if fromConversion != "" {
            let classStr = UnitType(str: fromConversion).rawValue
            guard let anyObjecType : AnyObject.Type = NSClassFromString("\(AppConfig.AppName).\(classStr)")
                else {
                    if classStr == "EURO" || classStr == "Cup" {
                        return -1
                    } else {
                        return -3
                    }
            }
            let nsObjecType : NSObject.Type = anyObjecType as! NSObject.Type
            
            guard let object = nsObjecType() as? Expression else {return -4 }
            return  object.getUnit(quantity, targetUnit: toConversion)
        
        }
        return -2
    }
    func getConversionFromInput(input:String) -> String {
        let partsOfQues = input.componentsSeparatedByString(" ")
        let fromConversion = partsOfQues[1]
        let toConversion = partsOfQues[3]
        let quantity = Double(partsOfQues[0])!
        
        let targetQuantity = getTargetQuantity(fromConversion, toConversion: toConversion, quantity: quantity)
        switch targetQuantity {
        case -1: return "Currently we only support conversion from RMB, USD, Gallon and Pint. \(fromConversion) is not supported"
        case -3: return "We can't recognize your input yet. Try something like RMB, rmb, Gallon, Gallons..."
        case -2: return "Sorry, we couldn't identify the source conversion. Try something like [ 10 Gallons to Pints ]"
        case -4: return "Couldn't convert. The object we created doesn't conform with Expression protocol"
        case let x where x >= 0 :  return "\(partsOfQues[0]) \(partsOfQues[1]) equals \(targetQuantity) \(partsOfQues[3])"
        default: return "Some other unknow error happened "
        }
    }
}

func getOutput(input: String) -> String {
    print("Getting user input:[ \(input) ]")
    let context = ConversionContext.sharedInstance
    return context.getConversionFromInput(input)
}

// Testing. User inputs a sentence and the conversionContext will translate it
let userInput = [
    "10 Gallons is Pints",
    "231 Gallo is Pints",
    "32.11 Pints in Gallon",
    "2001.99 USD to RMB",
    "984.2323 rmb in EURO",
    "23 Euro in RMB"
]
userInput.map{ print("Output: [ \(getOutput($0)) ] \n") }

interpretor