# AppleScript Calculator: Take selected text equation & calculate the result

I want to create a Service that is able to take the selected text, automatically perform the desired mathematical operation, and then paste the result directly after the selected text. This Service is similar to Google Search's built-in calculator function, but it is more handy.

Here are some examples:

if this is selected text : then this is new text

43+957 : = 1000

763-9482 : = -8719

8*26 : = 208

83/23 : = 3.60869565217

The above operations include addition, subtraction, multiplication, and division. So far, this script is not difficult to write.

But, I also would like the ability to make use of parentheses in the calculations. This is where the road gets rocky.

Here are some examples of equations that involve parentheses:

(4+55)/2 : = 29.5

352+((76.031*57/100)+(93.6*87/100)) : = 476.76967

(45+36+(64*0.04)+152+33+90)*(1/(1.98-425-(0.25*629)+431)) : = -2.40209017217

Yikes.

Okay. Baby steps...

Here is the code that I have written. It can only handle the first set of examples:

set inputString to "43 + 555 /4 *122"

-- Remove any and all spaces from this string
set inputString to replace_chars(inputString, " ", "")

-- Convert every instance of "x" to "*"
if (inputString contains "x") then
set inputString to replace_chars(inputString, "x", "*")
end if

-- Ensure that the string contains no foreign characters:
set supportedCharacters to {"1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "+", "-", "*", "/"}

set x to 1
set everyCharacterInInputStringIsValid to true

repeat until (x > (length of inputString))
if supportedCharacters contains (character x of inputString) then
set x to (x + 1)
else
set everyCharacterInInputStringIsValid to false
display dialog "Input string contains invalid character: " & (character x of inputString)
error number -128 (* user cancelled *)
end if
end repeat

-- String is all good. Now for the "fun" part.

set AppleScript's text item delimiters to {"+", "-", "*", "/"}
set onlyTheNumbers to text items of inputString
set AppleScript's text item delimiters to {""}
-- return onlyTheNumbers -- {"43", "555", "4", "122"}

set AppleScript's text item delimiters to onlyTheNumbers
set onlyTheSymbols to text items of inputString
set AppleScript's text item delimiters to {""}
-- return onlyTheSymbols -- {"", "+", "/", "*", ""}

-- Remove the first and last items in the onlyTheSymbols list
-- post #3 from http://macscripter.net/viewtopic.php?id=43371/
set removeSpecificItemsFromList to {1, (count of onlyTheSymbols)}
repeat with i in removeSpecificItemsFromList
set item i of onlyTheSymbols to null
end repeat
set onlyTheSymbols to every text of onlyTheSymbols

set calculatorBalance to ((0) as number)
set x to 1 as integer
set y to 2 as integer
set z to 1 as integer
set num1 to (((item x) of onlyTheNumbers) as number)
repeat until ((z) is greater than (count of onlyTheSymbols))

set num2 to (((item (y)) of onlyTheNumbers) as number)
set symbol to ((item z) of onlyTheSymbols)
if (symbol is "+") then
set calculatorBalance to (num1 + num2)
else if (symbol is "-") then
set calculatorBalance to (num1 - num2)
else if (symbol is "*") then
set calculatorBalance to (num1 * num2)
else if (symbol is "/") then
set calculatorBalance to (num1 / num2)
end if

set num1 to (calculatorBalance as number)

set y to (y + 1)
set z to (z + 1)
end repeat

display dialog " = " & calculatorBalance

on replace_chars(this_text, search_string, replacement_string)
set AppleScript's text item delimiters to the search_string
set the item_list to every text item of this_text
set AppleScript's text item delimiters to the replacement_string
set this_text to the item_list as string
set AppleScript's text item delimiters to ""
return this_text
end replace_chars


I am confused as to how to approach the whole concept of parentheses in this script. Can someone lend a hand?

Here's my rough idea:

1. Determine how many parentheses are in the string.

2. Split the input string into segments. The number of segments is based on the total number of parentheses in the string, minus one. The number of segments should be an odd number.

3. Check to see if any of the segments contain embedded parentheses.

4. Repeat steps 1 through 4 until no parentheses exist in any segment.

5. Now that you have your segments, act as if each segment is its own string. That is, calculate the result of each segment independently, instead of simply using the previous number in the list to interact with the following number in the list.

6. Add all of the segment results together.

I'm not sure if I have the right idea.

I am aware that I have wholly ignored the concept of order of operations. I don't know how to implement this exactly, either. My defense is that this is basically a part of the parentheses code. E.g., 1+4*2 would first have to be converted to 1+(4*2) before the result could be calculated.

Note:

I stated that I want the input for the AppleScript to be the currently selected text. But, you will clearly see that I have not written the code in this way.

For the purposes of writing, debugging, and troubleshooting this script, I am ignoring that element of the script, because this is very easy part to write and makes testing the code more complicated.

Once the script has been perfected, I will simply set the Service to receive selected text in any application. For the output, I will tell the script to key code and then paste the final result.

By the way, if these elaborate AppleScript questions are getting to be a bit overboard for Ask Different, then please let me know and I shall take them to a more AppleScript-centric site (e.g., MacScripter.net). I don't know if I am pushing my limit here.

• The key topic here is to find a way to parse formulas correctly and efficiently. AppleScript is just a tool to get that done (probably not the best but it has all the things needed). Commented Mar 13, 2017 at 8:18
• Is this an academic exercise to learn more about AppleScript or do you need to create this service for practical use? Commented Mar 13, 2017 at 12:51
• @GrahamMiln Not an academic exercise at all. I want to actually use this AppleScript as my go-to calculator. Commented Mar 13, 2017 at 12:55
• Please can you narrow down your question. A complete parser in one question is unlikely to attract good answers. Instead ask about how to iterate over characters, maintain a stack of opening and closing brackets, or other more specific problems you are facing. Each can then be answered individually and may be more helpful to many others learning AppleScript. Commented Mar 13, 2017 at 13:18
• stackoverflow.com/questions/28256/… provides a good insight into the design choices available – regardless of implementation language. Commented Mar 13, 2017 at 14:35

Over on StackOverflow you will find these related questions:

For a discussion about parsing expressions, see:

# The Burden of AppleScript

AppleScript is not a language to relish writing a parser in. The language is great for many things but not text manipulation. The language is Turing Complete, so your project is possible, but difficult.

AppleScript is an Open Scripting Architecture (OSA) dialect and maybe you would be willing to consider another OSA dialect for this project, such as JavaScript. While JavaScript is not ideal, you will find more resources and guides to help you. You can also mix and match Script Editor compiled AppleScript and JavaScript scripts.

# Use bc

Consider piping the selected text to bc and returning the result:

set theInput to "(4.0+55.0)/2.0"
set myCalculation to "echo 'scale=1;" & (theInput) & "' | bc"
set myResult to do shell script myCalculation


This approach avoids reinventing the wheel and uses the built-in command line calculator included with macOS, bc.

• Wow, that's handy. I'm grateful that you introduced me to bc, as using it drastically simplifies my code. You cautioned that I might not attract good answers...I would consider this a very good answer! Commented Mar 14, 2017 at 21:00

Creating a system-wide Service to replace a selected equation with its result

[update 18.10.2019]

I've found it useful to keep the equation and add the result, rather than replacing the equation with the result.

Here's the script that I use (the rest of the answer is unchanged):

on run {input, parameters}

set myCalculation to "echo 'scale=1;" & (input) & "' | bc"
set myResult to do shell script myCalculation
set myResult to input & "=" & myResult

set d to AppleScript's text item delimiters --get original delimiters
set AppleScript's text item delimiters to {return & linefeed, return, linefeed}
set fullResult to myResult's text items
set AppleScript's text item delimiters to ""
set fullResult to fullResult as text
set AppleScript's text item delimiters to d --replace original delimite

return fullResult
end run


Thanks to Nigel Garvey for the technique to remove carriage returns.

Using Automator, create a new Workflow (which used to be called a Service).

Drag Run AppleScript from the left pane to the right pane and paste the following program (replacing what's already there):

on run {input, parameters}
set myCalculation to "echo 'scale=1;" & (input) & "' | bc"
set myResult to do shell script myCalculation
return myResult
end run


Verify that the parameters in the top of the right pane say:

Worflow receives current text in any application
Output replaces selected text


Save the Worflow. Now there will be a service in every app's application menu for evaluating selected text and replacing it with the result.