I don't think it's worth it to support variadic operators in RPN. My trusty HP 48 certainly doesn't bother with it.
If you want algebraic expression parsing, many years ago (like nearly 30 years ago) I wrote a recursive-descent parser to parse expressions (one character lookahead I think), roughly based on operator precedences of my scientific calculator at the time. It might form something you could modify to work with cells as variables. I got it working with FB using the qb language dialect. I had to pull two functions from the C runtime library, but other than that it's pure QB dialect FB code.
If I were to do it again, I'd make a tokenizer and then have the parser work on tokens rather than have the parser do that in an ad hoc fashion.
Here it is, if you're interested in it. You could modify the Factor#() function to reference cells as variables (there's a read-only "X" variable defined for an example). Of course the tricky bit with a spreadsheet is you have to maintain a dependency tree so that your recalculations happen in the right order. This requires a bit of help from the expression parser to know what the dependencies are.
Code: Select all
'This is public domain code. Use it however you see fit. No attribution
'required.
DEFINT A-Z
DECLARE Sub GetNextChar
DECLARE Sub GetChr
DECLARE Function GetFunction$
DECLARE Function Factorial#(Number&)
DECLARE Function Solve#(Eq$)
DECLARE Function Expression#()
DECLARE Function Rank12#()
DECLARE Function Rank11#()
DECLARE Function Rank10#()
DECLARE Function Rank9#()
DECLARE Function Rank8#()
DECLARE Function Rank6#()
DECLARE Function Rank5#()
DECLARE Function Rank3#()
DECLARE Function Rank2#()
DECLARE Function Factor#()
'import from c standard library
DECLARE Function log10 cdecl ALIAS "log10f" (x as single) AS single
Declare Function exp10 cdecl ALIAS "exp10f" (x as single) AS single
DIM Shared NextChar$, SyntaxE, Begin, CPos, DivError, Equation$, DP
DIM Shared LBracket, RBracket, Func$
Dim SHARED pi as Double, Ans as Double
Dim SHARED x as Double
x=5
CONST True=1
CONST False=0
Do
Input "Eq: ",Eq$
If Eq$<>"quit" and eq$<>"" Then
A#=Solve#(Eq$)
If SyntaxE=True Then
Print "Syntax Error at position";CPos
ElseIf DivError<>False Then
Print "Mathematical Error"
Else
Print A#:Ans=A#
End If
End If
SyntaxE=False
DivError=False
Loop Until Eq$="quit"
Sub GetNextChar
Do
CPos = CPos + 1
If CPos>Len(Equation$) Then
NextChar$=Chr$(0)
Else
NextChar$=Mid$(Equation$,Cpos,1)
End If
Loop While NextChar$=" "
End Sub
Sub GetChr
CPos = CPos + 1
If CPos>Len(Equation$) Then
NextChar$=Chr$(0)
Else
NextChar$=Mid$(Equation$,Cpos,1)
End IF
End Sub
Function GetFunction$
If Instr("%*+-/<=>[\]^ûýüóò",NextChar$)>0 Then
Funct$=NextChar$
GetNextChar
Else
While Instr("%)(*+-./0123456789<=>[\]^ûüýòó "+Chr$(0),NextChar$)=0
Funct$=Funct$+NextChar$
GetChr
Wend
If NextChar$=" " Then GetNextChar
End If
GetFunction$=Funct$
End Function
Function Factorial#(Number&)
If Number&>1 Then
N#=Number&*Factorial#(Number&-1)
Else
N#=1
End If
Factorial#=N#
End Function
Function Solve#(Eq$)
Dim Value as Double
' On Local Error Goto ErrorTrap
Equation$=Eq$
CPos=0
GetNextChar
Solve#=Expression#
Exit Function
'ErrorTrap:
' If Err Then DivError=True
' Resume TheEnd
' TheEnd:
' Solve#=0
End Function
Function Expression#()
Dim Value as Double
Value=Rank12
Do '** DO loops mean left to right execution
If Func$="" Then
Select Case NextChar$
Case "A" to "Z", "a" to "z"
Func$=UCase$(GetFunction$)
Case Else
Expression#=Value
Exit Function
End Select
End If
Select Case Func$
Case "OR"
Func$=""
Value=Value OR Rank12
Case "XOR"
Func$=""
Value=Value XOR Rank12
Case Else
Expression#=Value
Exit Function
End Select
Loop
End Function
Function Rank12# ()
Dim Value as Double
Value=Rank11
Do
If Func$="" Then
Select Case UCase$(NextChar$)
Case "A" to "Z"
Func$=Ucase$(GetFunction$)
Case Else
Rank12#=Value
Exit Function
End Select
End If
If Func$="AND" Then
Func$=""
Value=Value And Rank11
Else
Rank12#=Value
Exit Function
End If
Loop
End Function
Function Rank11# ()
Dim Value as Double
Value=Rank10
Do
If Func$="" Then
Select Case NextChar$
Case "<"
GetNextChar
Select Case NextChar$
Case ">"
GetNextChar
Value=(Value<>Rank10)
Case "="
GetNextChar
Value=(Value<=Rank10)
Case Else
Value=(Value<Rank10)
End Select
Case ">"
GetNextChar
If NextChar$="=" Then
GetNextChar
Value=(Value>=Rank10)
Else
Value=(Value>Rank10)
End If
Case "="
GetNextChar
If NextChar$=">" Then
GetNextChar
Value=(Value>=Rank10)
Else
Value=(Value=Rank10)
End If
Case Else
Rank11#=Value
Exit Function
End Select
Else
Rank11#=Value
Exit Function
End If
Loop
End Function
Function Rank10# ()
Dim Value as Double
Value=Rank9
Do
If Func$="" Then
Select Case NextChar$
Case "+"
GetNextChar
Value=Value+Rank9#
Case "-"
GetNextChar
Value=Value-Rank9#
Case Else
Rank10#=Value
Exit Function
End Select
Else
Rank10#=Value
Exit Function
End If
Loop
End Function
Function Rank9#()
Dim Value as Double
Dim Divisor as Double
Value=Rank8
Do
If Func$="" Then
Select Case NextChar$
Case "*"
GetNextChar
value=value*Rank8
Case "/","\"
Div$=NextChar$
GetNextChar
Divisor=Rank8
If Div$="\" Then
Value=Value\Divisor
Else
Value=Value/Divisor
End If
Case Else
Rank9#=Value
Exit Function
End Select
Else
Rank9#=Value
Exit Function
End If
Loop
End Function
Function Rank8# ()
Dim Value as Double
Value=Rank6 'Rank7
Do
OldCPos=Cpos
If Func$="" Then
Select Case Ucase$(NextChar$)
Case "P","C"
Func$=Ucase$(GetFunction$)
Case Else
Rank8#=Value
Exit Function
End Select
End If
Select Case Func$
Case "P"
Func$=""
Value=Factorial(int(Value))/Factorial(Value-Rank6)
Case "C"
Func$=""
R#=Rank6 'Rank7
Value=Factorial(int(Value))/Factorial(int(Value-R#))/Factorial(int(R#))
Case Else
Rank8#=Value
Exit Function
End Select
Loop
End Function
'Rank7=abbrieviated multiplication in front of #6 functions
Function Rank6# ()
Dim Value as Double
If Func$="" Then
Select Case UCase$(NextChar$)
Case "-"
GetNextChar
Rank6#=-Rank6
Exit Function
Case "+"
GetNextChar
Rank6#=Rank6#
Exit Function
Case "A" to "Z", "û"
Func$=Ucase$(GetFunction$)
Case Else
Rank6#=Rank5
Exit Function
End Select
End If
Select Case Func$
Case "SQRT","û"
Func$=""
T#=Rank6 '** eval right to left
If T#<0 Then
Rank6#=1
DivError=True
Else
Rank6#=SQR(T#)
End If
Case "CBRT"
Func$=""
Rank6#=Exp(Log(Rank6)/3)
Case "SIN"
Func$=""
Rank6#=Sin(Rank6*pi/180)
Case "COS"
Func$=""
Rank6#=Cos(Rank6*pi/180)
Case "TAN"
Func$=""
Rank6#=Tan(Rank6*pi/180)
Case "ATAN"
Func$=""
Rank6#=ATN(Rank6)*180/pi
Case "ASIN"
Func$=""
Rank6#=ASIN(Rank6)*180/pi
Case "ACOS"
Func$=""
Rank6#=ACOS(Rank6)*180/pi
Case "LOG"
Func$=""
Rank6#=Log10(Rank6)
Case "ALOG"
Func$=""
Rank6#=Exp10(Rank6)
Case "LN"
Func$=""
Rank6#=Log(Rank6)
Case "EXP"
Func$=""
Rank6#=Exp(Rank6)
Case "INT"
Func$=""
Rank6#=Int(Rank6)
Case "FRAC"
Func$=""
n#=Rank6
Rank6#=n#-int(n#)
Case Else
Rank6#=Rank5
End Select
End Function
Function Rank5# () '** abbrieviated multiplying befor pi, brackets, etc
Dim Value as Double
If Func$="" Then
If NextChar$="ã" Then
OldCPos=CPos
GetNextChar
If Instr("*+-/;:<=>?@{|}"+Chr$(0),NextChar$)=0 Then
Rank5=pi*Rank3
Exit Function
Else
CPos=OldCPos-1
GetNextChar
End If
End If
End If
Value=Rank3
Do
If Func$="" Then
Select Case NextChar$
Case "0" to "9","."
Value=Value*Rank3
Case "(","ã" 'Abbrieviated multiplying in front of brackets
Value=Value*Rank3
Case Else
Rank5=Value
Exit Function
End Select
Else
Rank5=Value
Exit Function
End If
Loop
End Function
Function Rank3# ()
Dim Value as Double
Value=Rank2
If Func$="" Then
Select Case UCase$(NextChar$)
Case "^"
GetNextChar
value=value^Rank3# 'right to left
Case "P"
Func$=UCase$(GetFunction$)
End Select
End If
If Func$="PWRT" Then
Func$=""
value=EXP(Log(Rank3#)/value)
End If
Rank3#=Value
End Function
Function Rank2# ()
Dim Value as Double
Value=Factor
If Func$="" Then
Select Case NextChar$
Case "ý"
GetNextChar
Value=Value^2
Case "!"
Value=Factorial(Int(Value))
End Select
End If
Rank2#=Value
End Function
Function Factor# ()
Dim Value as Double
Dim C as Integer, I as integer
If Func$="" and (Asc(NextChar$)<=57 and Asc(NextChar$)>=48) Then
While Asc(NextChar$)<=57 and Asc(NextChar$)>=48
Value=Value*10+Val(NextChar$)
GetNextChar
If DP>0 Then DP = DP + 1
If NextChar$="." Then
If Dp=0 Then
GetNextChar
DP=1
Else
SyntaxE=True
Factor#=1
Exit Function
End If
End IF
If UCase$(NextChar$)="E" Then
GetNextChar
OldDp=Dp:Dp=0
Value=Value*10^Expression
Dp=OldDp
End If
Wend
For i=2 to DP
value=value/10
Next
DP=False%
Factor#=Value
Exit Function
Else
If Func$="" Then
Select Case Ucase$(NextChar$)
Case "ã"
GetNextChar
Factor#=pi
Exit Function
Case "P","X","A"
Func$=Ucase$(GetFunction$)
Case "("
GetNextChar
Value=Expression
GetNextChar
Factor#=Value
DP=False
Exit Function
Case "."
DP=1
GetChr
If Asc(NextChar$)>47 and Asc(NextChar$)<58 Then
Factor=Factor 'we have a number starting with a decimal place
Exit Function
Else
SyntaxE=True
End If
Case Else
SyntaxE=True
Factor#=1
Exit Function
End Select
End If
Select Case Func$
Case "PI"
Func$=""
Factor#=pi
Case "X"
Factor#=x
Func$=""
Case "ANS"
Func$=""
Factor#=Ans
Case Else
SyntaxE=True
Factor#=1
End Select
End If
End Function
Basically the top level of parsing is the expression, and below that are ranks of operator precedence, some are left to right associative, such as addition, subtraction, etc, and some are right to left. The lowest level is Factor#(), which is where you'd reference cells and variables.