Skip to content

Instantly share code, notes, and snippets.

@reisdev
Created May 27, 2022 01:44
Show Gist options
  • Save reisdev/6482f868ff876934c94896e94a6cb314 to your computer and use it in GitHub Desktop.
Save reisdev/6482f868ff876934c94896e94a6cb314 to your computer and use it in GitHub Desktop.
Handmade function to evaluate math expressions within strings
func calc(_ expression: String) -> Double {
var elements: [String] = []
let operators = ["*","/","+","-"]
var current = ""
for item in expression {
let char = String(item)
let last = elements.last ?? ""
if char == " " {
continue
} else if Double(char) != nil || char == "." ||
(char == "-" && current.count == 0 && (last == "" || operators.contains(last))) {
current += char
} else {
if(current.count > 0) {
elements.append(current)
current = ""
}
elements.append(char)
}
}
if current.count > 0 {
elements.append(current)
}
var element = ""
var i = 0, result = 0.0
if elements.contains("(") {
i = elements.lastIndex { $0 == "(" } ?? 0
} else if elements.contains("*") || elements.contains("/") {
i = (elements.firstIndex { $0 == "*" || $0 == "/" } ?? 0)
}
while i < elements.count {
element = elements[i]
if operators.contains(element) {
var left: Double? = nil, right: Double? = nil
if i > 0 {
left = Double(elements[i-1])
}
if i < elements.count - 1 {
right = Double(elements[i+1])
}
if let leftNumber = left,let rightNumber = right {
switch element {
case "+":
result = leftNumber + rightNumber
case "-":
result = leftNumber - rightNumber
case "*":
result = leftNumber * rightNumber
case "/":
result = leftNumber / rightNumber
default: break
}
elements.removeSubrange(i-1...i+1)
elements.insert(String(result),at: i-1)
i = 0
continue
} else if let rightNumber = right {
if element == "-" {
result = -rightNumber
} else {
result = rightNumber
}
elements.removeSubrange(i...i+1)
elements.insert(String(result),at: i)
i = 0
continue
} else {
i += 1
continue
}
} else if element == "(" {
let closingIndex = elements[i..<elements.count].firstIndex { $0 == ")" } ?? (elements.count-1)
let subExpression = elements[i+1..<closingIndex]
let result = calc(subExpression.joined(separator: ""))
elements.removeSubrange(i...closingIndex)
elements.insert(String(result),at: i)
i = 0
}
if elements.contains("(") {
i = elements.lastIndex { $0 == "(" } ?? 0
} else if elements.contains("*") || elements.contains("/") {
i = (elements.firstIndex { $0 == "*" || $0 == "/" } ?? 0)
} else {
i+=1
}
}
result = Double(elements.first ?? "") ?? 0.0
if elements.count > 1 {
result = elements.reduce(0) { (partial: Double, next: String) in
return partial + (Double(next) ?? 0.0)
}
}
return result
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment