Hi,
As part of a project I'm working on, I've had to write a string expression evaluator, based on some old C# code I used. As I develop it further and tidy it up I'll update this post.
Use it like this:
result# = Parse("sin((3+2)*10)")
AGK doesn't have exception handling, so you'll just need to be careful and check the parserError string. Here's the code:
global parserPos as integer
global parserOperators as string = "+-*/^%()"
global parserLetters as string = "abcdefghijklmnopqrstuvwxyz_"
global parserDigits as string = "0123456789"
global parserOutput as string = ""
global numberMarker as string = "#"
global operationMarker as string = "?"
global functionMarker as string = "@"
global variableMarker as string = "$"
global parserStack as string[]
global parserError as string
function Parse(expression as string)
parserError = ""
expression = FormatString(expression)
expression = ConvertToRPN(expression)
expression = Calculate(expression)
r# = ValFloat(expression)
endfunction r#
function Calculate(expression as string)
parserPos = 1
parserStack.Length = -1
while parserPos <= Len(expression)
token$ = LexicalAnalysisRPN(expression)
SyntaxAnalysisRPN(token$)
endwhile
if parserStack.Length > 0
exitfunction "0"
parserError = "Illegal format"
else
exitfunction parserStack[0]
endif
endfunction ""
function SyntaxAnalysisRPN(token as string)
rst as float
if Left(token, 1) = numberMarker
parserStack.Insert(Right(token, Len(token) - 1))
elseif NumberOfArguments(token) = 1
arg as float
arg = ValFloat(parserStack[parserStack.Length])
parserStack.Remove()
t$ = Right(token, Len(token) - 1)
if t$ = "un+"
rst = arg
elseif t$ = "un-"
rst = -arg
elseif t$ = "sqrt"
rst = Sqrt(arg)
elseif t$ = "sin"
rst = Sin(arg)
elseif t$ = "cos"
rst = Cos(arg)
elseif t$ = "tan"
rst = Tan(arg)
elseif t$ = "atan"
rst = ATan(arg)
elseif t$ = "asin"
rst = ASin(arg)
elseif t$ = "acos"
rst = ACos(arg)
elseif t$ = "log"
rst = Log(arg)
elseif t$ = "abs"
rst = Abs(arg)
endif
parserStack.Insert(Str(rst))
else
arg2 as float
arg1 as float
arg2 = ValFloat(parserStack[parserStack.Length])
parserStack.Remove()
arg1 = ValFloat(parserStack[parserStack.Length])
parserStack.Remove()
t$ = Right(token, Len(token) - 1)
if t$ = "+"
rst = arg1 + arg2
elseif t$ = "-"
rst = arg1 - arg2
elseif t$ = "*"
rst = arg1 * arg2
elseif t$ = "/"
if arg2 = 0
rst = 0
parserError = "Divide by zero"
else
rst = arg1 / arg2
endif
elseif t$ = "^"
rst = Pow(arg1, arg2)
elseif t$ = "%"
rst = Mod(arg1, arg2)
endif
parserStack.Insert(Str(rst))
endif
endfunction
function NumberOfArguments(token as string)
t$ = Right(token, Len(token) - 1)
if t$ = "+" or t$ = "-" or t$ = "*" or t$ = "/" or t$ = "^" or t$ = "log" or t$ = "%"
exitfunction 2
else
exitfunction 1
endif
endfunction 0
function LexicalAnalysisRPN(expression as string)
token$ = ""
token$ = token$ + Mid(expression, parserPos, 1)
parserPos = parserPos + 1
ch$ = Mid(expression, parserPos, 1)
while parserPos <= Len(expression) and ch$ <> numberMarker and ch$ <> operationMarker and ch$ <> functionMarker
token$ = token$ + ch$
parserPos = parserPos + 1
ch$ = Mid(expression, parserPos, 1)
endwhile
endfunction token$
function ConvertToRPN(expression as string)
parserPos = 1
parserStack.Length = -1
parserOutput = ""
while parserPos <= Len(expression)
token$ = LexicalAnalysisInfixNotation(expression)
parserOutput = SyntaxAnalysisInfixNotation(token$)
endwhile
while parserStack.Length > -1
if Left(parserStack[parserStack.Length], 1) = operationMarker
parserOutput = parserOutput + parserStack[parserStack.Length]
parserStack.Remove()
else
parserError = "Illegal format"
exitfunction ""
endif
endwhile
endfunction parserOutput
function SyntaxAnalysisInfixNotation(token as string)
if Left(token, 1) = numberMarker
parserOutput = parserOutput + token
elseif Left(token, 1) = functionMarker
parserStack.Insert(token)
elseif token = operationMarker + "("
parserStack.Insert(token)
elseif token = operationMarker + ")"
elem$ = parserStack[parserStack.Length]
parserStack.Remove()
while elem$ <> operationMarker + "("
parserOutput = parserOutput + elem$
elem$ = parserStack[parserStack.Length]
parserStack.Remove()
endwhile
if parserStack.Length > -1
if Left(parserStack[parserStack.Length] , 1) = functionMarker
parserOutput = parserOutput + parserStack[parserStack.Length]
parserStack.Remove()
endif
endif
else
while parserStack.Length > -1
if TokenPriority(token, parserStack[parserStack.Length]) = 1
parserOutput = parserOutput + parserStack[parserStack.Length]
parserStack.Remove()
else
exit
endif
endwhile
parserStack.Insert(token)
endif
exitfunction parserOutput
endfunction ""
function LexicalAnalysisInfixNotation(expression as string)
token$ = ""
ch$ = ""
token$ = token$ + Mid(expression, parserPos, 1)
if FindString(parserOperators, token$) > 0
isUnary = 0
if parserPos = 1
isUnary = 1
elseif parserPos > 1
if Mid(expression, parserPos - 1, 1) = "("
isUnary = 1
endif
endif
parserPos = parserPos + 1
if token$ = "+"
if isUnary = 1
exitfunction operationMarker + "un+"
else
exitfunction operationMarker + "+"
endif
elseif token$ = "-"
if isUnary = 1
exitfunction operationMarker + "un-"
else
exitfunction operationMarker + "-"
endif
else
exitfunction operationMarker + token$
endif
elseif FindString(parserLetters, token$) > 0
parserPos = parserPos + 1
while parserPos <= Len(expression)
ch$ = Mid(expression, parserPos, 1)
if FindString(parserLetters, ch$) > 0 or FindString(parserDigits, ch$) > 0
token$ = token$ + ch$
parserPos = parserPos + 1
else
exit
endif
endwhile
exitfunction functionMarker + token$
elseif FindString(parserDigits, token$) > 0
dp = 0
parserPos = parserPos + 1
while parserPos <= Len(expression)
ch$ = Mid(expression, parserPos, 1)
if FindString(parserDigits, ch$) > 0 or ch$ = "."
if ch$ = "."
dp = dp + 1
endif
token$ = token$ + ch$
parserPos = parserPos + 1
else
exit
endif
endwhile
if dp <= 1
exitfunction numberMarker + token$
else
parserError = "Bad float"
endif
endif
endfunction "0"
function FormatString(expression as string)
bop = 0
result$ = ""
expression = Lower(expression)
for i = 1 to Len(expression)
ch$ = Mid(expression, i, 1)
if ch$ = "("
bop = bop + 1
elseif ch$ = ")"
bop = bop - 1
endif
if ch$ = " "
else
result$ = result$ + ch$
endif
next i
endfunction result$
function TokenPriority(token as string, p as string)
if IsRightAssociated(token) = 1
if GetTokenPriority(token) < GetTokenPriority(p)
exitfunction 1
endif
else
if GetTokenPriority(token) <= GetTokenPriority(p)
exitfunction 1
endif
endif
endfunction 0
function IsRightAssociated(token as string)
if token = operationMarker + "^"
exitfunction 1
endif
endfunction 0
function GetTokenPriority(token as string)
if token = operationMarker + "("
exitfunction 0
elseif token = operationMarker + "+" or token = operationMarker + "-"
exitfunction 2
elseif token = operationMarker + "un+" or token = operationMarker + "un-"
exitfunction 6
elseif token = operationMarker + "*" or token = operationMarker + "/"
exitfunction 8
else
exitfunction 10
endif
endfunction -1