Sometimes the calculator that ships with OS X doesn’t quiet hack it, because you want to alter the formula, and a spreadsheet doesn’t either quite fit the bill, since you also want to see the formula. And as with the calculator, the previous results disappears as easily as with the former. Now, I could have done all my calculations, at least most of them in a terminal window. But I am not in a terminal window all the time, and I would miss out on the trigonometric and expontial functions anyway, so I was reluctant to use it, and was wondering if it was time to by soulver, that I suppose does this job perfectly.
But then again, my idea was quite different, as I am lazy, I wouldn’t like to first select text, copy it, edit it, and then run the program again. Copy a calculation, edit it, and then run the script, and have the result show up below, is also too much. I figured out the uttermost laziness would be to have the script insert both the line that was computed in its original form, but with a ! (bang) in front of it, and the result on a line below, prepended by a colon.
Edit
I have the added ability to “define” variables, see the last post for an example.
Example:
(4*3)+3*(4-1)-3
! (4*3)+3*(4-1)-3
: 42
If I run it again, and it sees that the line is unaltered, then it should leave the result be, exactly like it was. -It is important that you don’t insert any newlines/returns after your calculations, unless you haven’t edited it.
Here I have edited the original calculation like so:
(4*5)+5*(6-1)-5
! (4*3)+3*(4-1)-3
: 42
After I have run the script, it looks like this:
(4*5)+5*(6-1)-5
! (4*5)+5*(6-1)-5
: 40
! (4*3)+3*(4-1)-3
: 42
I also want to be able to run the script onto documents that contains text, so we need to drop those lines from calculations, which leads to a little problem when we are dealing sines and logs in the front of our lines, to be dead sure it works, we start such lines with 1* so that the script understand that it is all about math.
Example:
1*sin(pi)
!1*sin(pi)
:0
I figured this was a good idea, but I hadn’t quite come around to implement it,
when I had a strike of luck:
While cleaning my Mac, for the upgrade to Maverick I came across a script I had downloaded for BBEdit: Calculate, that are made by Dennis Rande 3/3/7.
This works on a selection, but the core meat of it I can use in a script.
Here are his examples, but as of now, you may perform many calcuations at once
in a document. In fact, everything that looks like a calculation will be calculted, so be sure to have a character that is not amongst number and operators
before a calculation takes place (spaces and tabs) will cut it, so a ! or something is requisite in front of a “formula” you want to keep!
Further Examples:
"5+5/(3-1)"
"1*sqrt(3**2 + 4**2)"
"1*sin(3.1415926/2)"
"0x2FF7" -- converts hex to dec
"1*rand" -- returns a random number
"1*time" -- returns seconds since the Unix Epoch
Recommend short-cut key: cmd-K in TextEdit.
Enjoy
-- © 2013 McUsr after idea of Dennis Rande and put into public domain.
script mathEdit
property funcNames : {"sin(", "cos(", "ln(", "exp(", "tan(", "asin(", "acos(", "atan(", "atan2(", ")"}
local varlist
set varlist to {{"pi", pi as text}, {"degrad", (pi / 180) as text}}
# set end of varList to
set AppleScript's text item delimiters to ""
global valchars, tids
set valchars to "()1234567890.*-+/"
local csep
set csep to text 2 of ((1 / 2) as text)
if csep is "," then adjustValidChars()
tell application "TextEdit" to tell its document 1
-- We do keep track of the index of the paragraph we are working on.
set {Ix, Pc} to {1, count paragraphs of text of it}
if last character of its text is not return then
make new character at end of its text with data return
end if
if Pc = 0 then error number -128
-- Do some math on the paragraphs that are applicable in doc 1!
repeat
local isFound
set isFound to false
-- we grab the current paragraph.
local curMeat
set curMeat to paragraph Ix as text
-- sifts out whitespace
set workMeat to my cleansed(curMeat, tids)
-- Checks if there is something to be done with this par.
set aVar to my lookForVariableDef(workMeat, tids)
-- a variable is the only thing that is supposed to be on a line together with its value.
if item 1 of aVar is not "" then
set end of varlist to aVar
set workMeat to ""
else
-- check to see if the workMeat contains any variable names to be substituted!
set chunks to words of workMeat
set isFound to false
repeat with curChunk in chunks
if first character of curChunk is not in valchars then
repeat with kval in varlist
local tst
set tst to item 1 of kval
if contents of curChunk is item 1 of kval then
set isFound to true
set workMeat to my changeText(workMeat, item 2 of kval, item 1 of kval)
exit repeat
end if
end repeat
end if
end repeat
if csep = "," then set workMeat to my changeText(workMeat, ".", ",")
end if
-- we figure out if it is a valid execution line.
if my isALineWeCanCalculate(workMeat, funcNames, valchars) then
log "We can"
-- check to see if we are having a result line below, and if it is alike when the "!"
-- is removed.
local nextPar
if Ix < Pc then
if isFound and (Ix + 1) < Pc then
set nextPar to my cleansed((paragraph (Ix + 2) as text), tids)
else
set nextPar to my cleansed((paragraph (Ix + 1) as text), tids)
end if
-- does it start with a bang?
tell me -- !
set bangPos to offset of "!" in nextPar
end tell
if bangPos > 0 then
set nextPar to text (bangPos + 1) through -1 of nextPar
if csep = "," then set nextPar to my changeText(nextPar, ".", ",")
end if
else
set nextPar to ""
end if
if workMeat is not nextPar then
local calcResult, calcError, failureMsg
try
tell me
set calcResult to do shell script "perl -MMath::Trig -e 'print 0+" & workMeat & "'"
end tell
on error calcError
set calcResult to ""
if calcError ends with " at -e line 1." then set calcError to text 1 thru -15 of calcError
end try
if length of calcResult is 0 then
set failureMsg to ": Could not evaluate \"" & curMeat & "\""
if length of calcError is not 0 then
set failureMsg to failureMsg & "- Reason: " & calcError & return
end if
my insertPar(failureMsg, Ix)
set {Ix, Pc} to {Ix + 1, Pc + 1}
else
if csep = "," then set calcResult to my changeText(calcResult, ",", ".")
my insertPar("! " & curMeat, Ix)
set Ix to Ix + 1
if isFound then
if csep = "," then set workMeat to my changeText(workMeat, ",", ".")
my insertPar("! " & workMeat, Ix)
set Ix to Ix + 1
my insertPar(": " & calcResult, Ix)
set {Ix, Pc} to {Ix + 1, Pc + 3}
else
my insertPar(": " & calcResult, Ix)
set {Ix, Pc} to {Ix + 1, Pc + 2}
end if
end if
else if Ix < Pc then
-- we'll skip those two!
set Ix to Ix + 2
if Ix ≥ Pc then exit repeat
end if
if Ix ≥ Pc then exit repeat
else if Ix < Pc then
-- look at the next one
set Ix to Ix + 1
else
exit repeat
end if
end repeat
end tell
on isALineWeCanCalculate(lineToAssert, validFunctions, validChars)
-- if the line we was to assert wasn't empty
-- And the new line contains something valid after we have sifted it for
-- functions that are valid in perl. Then we return true.
if lineToAssert = "" then
return false
else
local tids
tell (a reference to AppleScript's text item delimiters)
set {tids, contents of it} to {contents of it, my funcNames}
set lineToAssert to text items of lineToAssert
set contents of it to ""
set lineToAssert to lineToAssert as text
set contents of it to tids
end tell
if first character of lineToAssert is in validChars then
return true
else
return false
end if
end if
end isALineWeCanCalculate
on lookForVariableDef(atextpar, tids)
local probe
set probe to words of atextpar
if (length of probe > 1) and item 2 of probe = "=" then
local theValue
set {theValue, isValid} to {cleansed(text ((offset of "=" in atextpar) + 1) thru -1 of atextpar, tids), true}
repeat with aChar in every character in theValue
if aChar is not in "-1234567890.," then
set isValid to false
exit repeat
end if
end repeat
if isValid then
return {item 1 of probe, theValue}
else
return {"", 0}
end if
else
return {"", 0}
end if
end lookForVariableDef
on changeText(atextpar, new, old)
local tids
tell (a reference to AppleScript's text item delimiters)
set tids to contents of it
set contents of it to old
set atextpar to text items of atextpar
set contents of it to new
set atextpar to atextpar as text
set contents of it to tids
end tell
return atextpar
end changeText
on cleansed(atextpar, tids)
tell (a reference to AppleScript's text item delimiters)
set contents of it to {space, tab, return, linefeed}
set atextpar to text items of atextpar
set contents of it to ""
set atextpar to atextpar as text
set contents of it to tids
end tell
return atextpar
end cleansed
on insertPar(thePar, Ix)
local probe
tell application "TextEdit"
tell text of its front document
set font_nm to (font of paragraph Ix)
set font_sz to (size of its paragraph Ix)
local ofs
-- if we are getting a line ending with return
make new paragraph at after its paragraph Ix with data thePar & return
tell (paragraph (Ix + 1))
set font of its every character to font_nm
set size of its every character to font_sz
end tell
end tell
end tell
end insertPar
on adjustValidChars()
tell (a reference to AppleScript's text item delimiters)
set {tids, contents of it} to {contents of it, "."}
set {my valchars, contents of it} to {text items of my valchars, ","}
set {my valchars, contents of it} to {my valchars as text, tids}
end tell
end adjustValidChars
end script
tell mathEdit to run