Elements. Build native projects for any modern development platform, using the language(s) of your choice. Oxygene (Object Pascal), C#, Swift, Java, Go. | RemObjects Software

Calculator.Engine

Language: Silver, Platform: All, Category: Calculator
https://github.com/remobjects/ElementsSamples/tree/master/Silver/All/Calculator/Calculator.Engine

$(MSBuildThisFileDirectory)Evaluator.swift

import RemObjects.Elements.RTL

enum EvaluatorTokenType: Int32 {
	case EOF
	case Number
	case Op_Add
	case Op_Sub
	case Op_Mul
	case Op_Div
	case Error
}

class EvaluatorError: Exception {
}

class EvaluatorToken {
	public init(token: EvaluatorTokenType, value: String, offset: Integer) {
		self.Token = token
		self.Value = value
		self.Offset = offset
	}

	public let Token: EvaluatorTokenType
	public let Value: String
	public let Offset: Integer
}

public class Evaluator {
	public init() {
	}
	private static let EOF = EvaluatorToken(token: EvaluatorTokenType.EOF, value: "", offset: 0)

	private var Tokens: List<EvaluatorToken>!
	private var Index: Int = 0

	// returns the current token, or EOF if at the end
	private var Current: EvaluatorToken { 
		if Tokens != nil && Index < Tokens.Count {
			return Tokens[Index] 
		}
		return EOF
	}

	// Evaluates a string expression like (1 + 2 * 4.5) and return the evaluated value
	public func Evaluate(_ input: String) -> Double {
		Tokens = Tokenize(input)
		Index = 0

		return ParseAdditionExpression()
	}

	// Parses + and - expressions, this is split from * and / so that 
	// + and - have a lower prescendence than * and /
	private func ParseAdditionExpression() -> Double {
		var l = ParseMultiplicationExpression()
		while Current.Token == EvaluatorTokenType.Op_Sub || Current.Token == EvaluatorTokenType.Op_Add {
			let sub = Current.Token == EvaluatorTokenType.Op_Sub
			Index += 1
			let r = ParseMultiplicationExpression()
			if (sub) {
				l = l - r
			} else {
				l = l + r
			}
		}
		return l
	}

	// Parse * and / 
	private func ParseMultiplicationExpression() -> Double {
		var l = ParseValueExpression()
		while Current.Token == EvaluatorTokenType.Op_Mul || Current.Token == EvaluatorTokenType.Op_Div {
			let mul = Current.Token == EvaluatorTokenType.Op_Mul
			Index += 1
			let r = ParseValueExpression()
			if (mul) {
				l = l * r
			} else {
				l = l / r
			}
		}
		return l
	}

	private func ParseValueExpression() -> Double {
		switch (Current.Token)
		{
			case EvaluatorTokenType.Op_Add: // Process +15 as unary
				Index += 1
				return ParseValueExpression()
			case EvaluatorTokenType.Op_Sub: // Process -15 as unary
				Index += 1
				return -ParseValueExpression()
			case EvaluatorTokenType.Number:
				var res = Current.Value
				Index += 1
				return Convert.ToDoubleInvariant(res)
			case EvaluatorTokenType.EOF:
				__throw EvaluatorError("Unexected end of expression")
			default:
				__throw EvaluatorError("Unknown value at offset \(Current.Offset)")
			
		}
	}
	// Splits the string into parts; skipping whitespace
	private static func Tokenize(_ input: String) -> List<EvaluatorToken> {
		var res = List<EvaluatorToken>()
		var input = input;
		// for parsing convenience so look ahead won't throw exceptions.
		input = input + "\0\0" 
		
		var i = 0
		while (i < input.Length) {
			var c: Int = i
			switch (input[i]){
				case "\0":
					i = input.Length
				case "0", "1","2","3","4","5","6","7","8","9",".":
					c = i + 1
					var gotDot = input[i] == "."
					while true {
						var ch = input[c]
						if ch == "0" || ch ==  "1" || ch == "2" || ch == "3" || ch == "4" || ch == "5" || ch == "6" || ch == "7" || 
						ch == "8" || ch == "9" || (!gotDot && ch == ".") {
							c += 1
							if (ch == ".") {
								gotDot = true
							}
						} else {
							break
						}
					}
					res.Add(EvaluatorToken(token: EvaluatorTokenType.Number, value: input.Substring(i, c-i), offset: i))
					i = c
				case "+": 
					res.Add(EvaluatorToken(token: EvaluatorTokenType.Op_Add, value: "+", offset: i))
					i += 1
				case "-": 
					res.Add(EvaluatorToken(token: EvaluatorTokenType.Op_Sub, value: "-", offset: i))
					i += 1
				case "*": 
					res.Add(EvaluatorToken(token: EvaluatorTokenType.Op_Mul, value: "*", offset: i))
					i += 1
				case "/": 
					res.Add(EvaluatorToken(token: EvaluatorTokenType.Op_Div, value: "/", offset: i))
					i += 1
				case " ", "\t", "\r", "\n": 
					i += 1
				default: 
					res.Add(EvaluatorToken(token: EvaluatorTokenType.Error, value: input[i], offset: i))
					i += 1 
			}
		}
		return res
	}
}