RPN is 'reverse polish notation'. For some it is well known from HP calculators.
This is the first Brainstorming version. I publish it in this very early state because of the Function Graph calculator from Michael Vogel.
viewtopic.php?t=85968
Because converting mathematical expressions into a RPN form might be the solution for compiling math expressions into a form what can be processed fast and easy.
My RPN version supports user defined parameters in an esay way. So it is possible to calculate expressions like
y = ax² + bx +c
The compild RPN Programm has only one type of command. A ProcedureCall with a PointerParameter to a value.
So each operation in the compiled RPN Programm consists of 2 Pointer valus. For processing that we do not need
any If statment. Just calling a Procedure with one Parameter thats all!
Module RPN : it generally works, but I'm sure it is not Error free! It's my Brainstorming version
Code: Select all
; ===========================================================================
; FILE : PbFw_Module_RPN.pb
; NAME : Module RPN Calculator Emulation [RPN::]
; DESC : RPN "Reverse polish notation" calculator functions
; DESC : For emulating a RPN Calculator or to process 'compiled' RPN programs
; DESC :
; ===========================================================================
;
; AUTHOR : Stefan Maag
; DATE : 2025/01/06
; VERSION : 0.1 Brainstorming Version
; COMPILER : PureBasic 6.0 and higher
;
; LICENCE : MIT License see https://opensource.org/license/mit/
; or \PbFramWork\MitLicence.txt
; ===========================================================================
; ChangeLog:
;{
;
;}
;{ TODO:
;}
; ===========================================================================
;- --------------------------------------------------
;- Include Files
; --------------------------------------------------
;XIncludeFile "PbFw_Module_PbFw.pb" ; PbFw:: FrameWork control Module
; XIncludeFile ""
DeclareModule RPN ; reverse polish notation
EnableExplicit
Enumeration ERPN_FUNCTION 0
; Fuctions without Parameter value
#RPN_CLR
#RPN_CLRALL
#RPN_PUSH
#RPN_POP
#RPN_ADD
#RPN_SUB
#RPN_MUL
#RPN_DIV
; Fuctions with Parameter value
#RPN_LD
#RPN_LD2
#RPN_LD_ADD
#RPN_LD_SUB
#RPN_LD_MUL
#RPN_LD_DIV
#RPN_LD_MIN
#RPN_LD_MAX
#RPN_LD_SQ ; Load Square : x²
#RPN_LD_CUB ; Load Cubic : x³
#RPN_LD_SQRT ; SquareRoot
; Memory Functions
#RPN_MCLR ; Clear Memory
#RPN_MADD ; Add to Memory
#RPN_MRET ; Return Memory to A1
; last Function
#RPN_GET ; GET has to be the last constant, because it is used to DIM Array RPN_ProcPtr(#RPN_GET)
EndEnumeration
Structure TRPN_PROG_ELEMENT
*pProc
*pValue.Double
EndStructure
Declare GetRpnProcPointer(ProcNo = #RPN_CLRALL)
Declare.i CallRPN(RpnFctNo = #RPN_CLR, *value.Double=0)
Declare.d GetResult()
Declare.d RunRpnProg(Array RPN_PROC.TRPN_PROG_ELEMENT(1), NoOfElements.i=#PB_Default)
EndDeclareModule
Module RPN
EnableExplicit
; RPN Processor Registers
; ----------
; M ; Memory Register for M+ Calculator function
; ----------
; A2 ; Akku 2
; ----------
; A1 ; Akku 1
; ----------
Structure TRpnRegisters
A1.d
A2.d
M.d
EndStructure
Global RPN.TRpnRegisters
Global Dim RPN_ProcPtr.i(#RPN_GET) ; the Array with the Pointers to all RPN_Functions
; This is the general Prototype function for calling any RPN_Function
; We same Prototype for all RPN_Functions, so we do not need any IF or SELECT statment to process
; compiled RPN programms. Any RPN Command in a compiled program consist of to values
; *pProc "Pointer to the RPN_Function : *pValue "Pointer to the Value as Double -> see the TRPN_PROG_ELEMENT Stuct
; A compiled RPN program is only an Array or a List of Pointers
Prototype InvokeUpnFkt(*value.Double)
Global InvokeUpnFkt.InvokeUpnFkt
Global _RpnDummy.Double
;- ----------------------------------------
;- Public
;- ----------------------------------------
Procedure.i GetRpnProcPointer(ProcNo = #RPN_CLRALL)
ProcedureReturn RPN_ProcPtr(ProcNo)
EndProcedure
Procedure CallRPN(RpnFctNo = #RPN_CLR, *value.Double=0)
If *value = 0
*value = _RpnDummy
EndIf
InvokeUpnFkt = RPN_ProcPtr(RpnFctNo)
InvokeUpnFkt(*value)
EndProcedure
Procedure.d GetResult()
ProcedureReturn RPN\A1
EndProcedure
Procedure.d RunRpnProg(Array RPN_PROC.TRPN_PROG_ELEMENT(1), NoOfElements.i=#PB_Default)
; process a RPN Program of multiple operations stored in an Array
Protected res.d, I, N
If NoOfElements = #PB_Default ; #PB_Default = -1
N = ArraySize(RPN_PROC())
ElseIf NoOfElements > (ArraySize(RPN_PROC()) + 1)
N = ArraySize(RPN_PROC())
Else
n = NoOfElements - 1
EndIf
For I = 0 To N
InvokeUpnFkt = RPN_PROC(I)\pProc
InvokeUpnFkt(RPN_PROC(I)\pValue)
Next
ProcedureReturn RPN\A1
EndProcedure
;- ----------------------------------------
;- Private
;- ----------------------------------------
Macro mac_DebugProc()
Debug #PB_Compiler_Procedure
EndMacro
Macro mac_DebugProcWithPara(Para)
Debug #PB_Compiler_Procedure + " : " + Para
EndMacro
Procedure CLR(*dummy) ; Clear Akku1/2
mac_DebugProc()
With RPN
\A1 = 0
\A2 = 0
EndWith
EndProcedure
RPN_ProcPtr(#RPN_CLR) = @CLR()
Procedure CLRALL(*dummy) ; Clear Akku1/2 and Mem
mac_DebugProc()
With RPN
\A1 = 0
\A2 = 0
\M = 0
EndWith
EndProcedure
RPN_ProcPtr(#RPN_CLRALL) = @CLRALL()
Procedure PUSH(*dummy) ; PUSH Akku1 to Akku2
mac_DebugProc()
With RPN
\A2 = \A1
EndWith
EndProcedure
RPN_ProcPtr(#RPN_PUSH) = @PUSH()
Procedure POP(*dummy) ; POP Akku2 to Akku 1
mac_DebugProc()
With RPN
\A1 = \A2
EndWith
EndProcedure
RPN_ProcPtr(#RPN_POP) = @POP()
Procedure ADD(*dummy) ; ADD A1 = A1 + A2
With RPN
\A1 = \A1 + \A2
mac_DebugProcWithPara(\A1)
EndWith
EndProcedure
RPN_ProcPtr(#RPN_ADD) = @ADD()
Procedure SUB(*dummy) ; SUB A1=A2-A1
With RPN
\A1 = \A2 + \A1
mac_DebugProcWithPara(\A1)
EndWith
EndProcedure
RPN_ProcPtr(#RPN_SUB) = @SUB()
Procedure MUL(*dummy)
With RPN
\A1 = \A2 * \A1
mac_DebugProcWithPara(\A1)
EndWith
EndProcedure
RPN_ProcPtr(#RPN_MUL) = @MUL()
Procedure DIV(*dummy)
With RPN
\A1 = \A2 / \A1
mac_DebugProcWithPara(\A1)
EndWith
EndProcedure
RPN_ProcPtr(#RPN_DIV) = @DIV()
Procedure LD(*value.Double) ; Load value to A1 and save old A1 in A2
mac_DebugProcWithPara(*value\d)
With RPN
\A2 = \A1
\A1 = *value\d
EndWith
EndProcedure
RPN_ProcPtr(#RPN_LD) = @LD()
Procedure LD2(*value.Double)
mac_DebugProcWithPara(*value\d)
With RPN
\A2 = *value\d
\A1 = *value\d
EndWith
EndProcedure
RPN_ProcPtr(#RPN_LD2) = @LD2()
Procedure LD_ADD(*value.Double) ; LOAD & ADD : save A1 in A2 : A1 = A1 + value
mac_DebugProcWithPara(*value\d)
With RPN
\A2 = \A1
\A1 = \A1 + *value\d
EndWith
EndProcedure
RPN_ProcPtr(#RPN_LD_ADD) = @LD_ADD()
Procedure LD_SUB(*value.Double) ; LOAD & SUB
mac_DebugProcWithPara(*value\d)
With RPN
\A2 = \A1
\A1 = \A1 - *value\d
EndWith
EndProcedure
RPN_ProcPtr(#RPN_LD_SUB) = @LD_SUB()
Procedure LD_MUL(*value.Double) ; LOAD & MUL
mac_DebugProcWithPara(*value\d)
With RPN
\A2 = \A1
\A1 = \A1 * *value\d
EndWith
EndProcedure
RPN_ProcPtr(#RPN_LD_MUL) = @LD_MUL()
Procedure LD_DIV(*value.Double) ; LOAD & DIV
mac_DebugProcWithPara(*value\d)
With RPN
\A2 = \A1
\A1 = \A1 / *value\d
EndWith
EndProcedure
RPN_ProcPtr(#RPN_LD_DIV) = @LD_DIV()
Procedure LD_MIN(*value.Double) ; LOAD & MIN : A1 = Min(A1, value)
mac_DebugProcWithPara(*value\d)
With RPN
If *value\d < \A1
\A2 = \A1 ; greater Value in A1 to A2
\A1 = *value\d ; lower value as result to A1
Else
\A1 = \A2
\A2 = *value\d
EndIf
EndWith
EndProcedure
RPN_ProcPtr(#RPN_LD_MIN) = @LD_MIN()
Procedure LD_MAX(*value.Double) ; LOAD & MAX
mac_DebugProcWithPara(*value\d)
With RPN
If *value\d > \A1
\A2 = \A1 ; lower Value in A1 to A2
\A1 = *value\d ; greater value as result to A1
Else
\A1 = \A2
\A2 = *value\d
EndIf
EndWith
EndProcedure
RPN_ProcPtr(#RPN_LD_MAX) = @LD_MAX()
Procedure LD_SQ(*value.Double) ; LOAD SQuare
mac_DebugProcWithPara(*value\d)
With RPN
\A2 = \A1
\A1 = *value\d * *value\d
EndWith
EndProcedure
RPN_ProcPtr(#RPN_LD_SQ) = @LD_SQ()
Procedure LD_CUB(*value.Double) ; LOAD & CUBic
mac_DebugProcWithPara(*value\d)
With RPN
\A2 = \A1
\A1 = *value\d * *value\d * *value\d
EndWith
EndProcedure
RPN_ProcPtr(#RPN_LD_CUB) = @LD_CUB()
Procedure LD_SQRT(*value.Double) ; LOAD & SQRT SquareRoot
mac_DebugProcWithPara(*value\d)
With RPN
\A2 = \A1
\A1 = *value\d * *value\d * *value\d
EndWith
EndProcedure
RPN_ProcPtr(#RPN_LD_SQRT) = @LD_SQRT()
Procedure MCLR(*dummy) ; CLEAR Memory
mac_DebugProc()
With RPN
\M = 0
EndWith
EndProcedure
RPN_ProcPtr(#RPN_MCLR) = @MCLR()
Procedure MADD(*dummy) ; M+ Function, Add to Memory
With RPN
\M = \M + \A1
mac_DebugProcWithPara(\M)
EndWith
EndProcedure
RPN_ProcPtr(#RPN_MADD) = @MADD()
Procedure MRET(*dummy) ; MR Function, Return Memory to A1
With RPN
\A2 = \A1
\A1 = \M
mac_DebugProcWithPara(\A1)
EndWith
EndProcedure
RPN_ProcPtr(#RPN_MRET) = @MRET()
Procedure GET(*Result.Double) ; Get the Caculation Result : *Result\d = A1
mac_DebugProcWithPara(*Result\d)
With RPN
*Result\d = \A1
EndWith
EndProcedure
RPN_ProcPtr(#RPN_GET) = @GET()
EndModule
CompilerIf #PB_Compiler_IsMainFile
; ----------------------------------------------------------------------
; M O D U L E T E S T C O D E
; ----------------------------------------------------------------------
EnableExplicit
UseModule RPN
Procedure CreateParableProg(Array RpnProg.TRPN_PROG_ELEMENT(1), *x, *a, *b, *c) ; creates the RPN Programm for a parabel function ax² + bx +c
; creates the compiled RPN Program for ; ax² + bx +c
Protected I
; --------------------------------------------------
; !Thats the Programm Script we have to compile
; --------------------------------------------------
; CLRALL ; Clear Akku1/2 and Mem
; --- ax² ---
; LD_SQ x ; Load x² to A1
; LD_MUL a ; Load & MUL a
; MADD ; Add result to Mem
; --- bx ---
; LD x
; LD_MUL b
; MRET ; Return ax² from Mem
; ADD ; Add to bx
; LD_ADD c ; +c
; --------------------------------------------------
Dim RpnProg(8) ; we need 9 operations 0..9
; Create an entry for each operation in the RpnProg - Array
; CLRALL
RpnProg(I)\pProc = GetRpnProcPointer(#RPN_CLRALL)
RpnProg(I)\pValue = 0
I + 1
; LD_SQ x ; Load x² to A1
RpnProg(I)\pProc = GetRpnProcPointer(#RPN_LD_SQ)
RpnProg(I)\pValue = *x
I + 1
; LD_MUL a ; Load & MUL a
RpnProg(I)\pProc = GetRpnProcPointer(#RPN_LD_MUL)
RpnProg(I)\pValue = *a
I + 1
; MADD ; Add result to Mem
RpnProg(I)\pProc = GetRpnProcPointer(#RPN_MADD)
RpnProg(I)\pValue = 0
I + 1
; LD x
RpnProg(I)\pProc = GetRpnProcPointer(#RPN_LD)
RpnProg(I)\pValue = *x
I + 1
; LD_MUL b
RpnProg(I)\pProc = GetRpnProcPointer(#RPN_LD_MUL)
RpnProg(I)\pValue = *b
I + 1
; MRET ; Return ax² from Mem
RpnProg(I)\pProc = GetRpnProcPointer(#RPN_MRET)
RpnProg(I)\pValue = 0
I + 1
; ADD ; Add to bx
RpnProg(I)\pProc = GetRpnProcPointer(#RPN_ADD)
RpnProg(I)\pValue = 0
I + 1
; LD_ADD c ; +c
RpnProg(I)\pProc = GetRpnProcPointer(#RPN_LD_ADD)
RpnProg(I)\pValue = *c
EndProcedure
Define.d y, x, a, b, c
Define P.Double
Define.i I
Debug "--------------------------------------------------"
Debug " Test all Function calls"
Debug "--------------------------------------------------"
; Fuctions with dummy Parameter
CallRPN(#RPN_CLR)
CallRPN(#RPN_CLRALL)
CallRPN(#RPN_PUSH)
CallRPN(#RPN_POP)
CallRPN(#RPN_ADD)
CallRPN(#RPN_SUB)
CallRPN(#RPN_MUL)
CallRPN(#RPN_DIV)
; Fuctions with Parameter value
CallRPN(#RPN_LD, P)
CallRPN(#RPN_LD2, P)
CallRPN(#RPN_LD_ADD, P)
CallRPN(#RPN_LD_SUB, P)
CallRPN(#RPN_LD_MUL, P)
CallRPN(#RPN_LD_DIV, P)
CallRPN(#RPN_LD_MIN, P)
CallRPN(#RPN_LD_MAX, P)
CallRPN(#RPN_LD_SQ, P)
CallRPN(#RPN_LD_CUB, P)
CallRPN(#RPN_LD_SQRT, P)
; Memory Functions
CallRPN(#RPN_MCLR, P)
CallRPN(#RPN_MADD, P)
CallRPN(#RPN_MRET, P)
; last Function
CallRPN(#RPN_GET, P)
Debug "--------------------------------------------------"
Debug " Test Loop(10) with x parameter "
Debug " y = x + c : x = {1..10} c=1"
Debug "--------------------------------------------------"
For I = 1 To 10
x = I
c = 1
CallRPN(#RPN_LD, @c)
CallRPN(#RPN_LD_ADD, @x)
Debug "Result = " + StrD(GetResult())
Debug ""
Next
Debug "--------------------------------------------------"
Debug " Test compiled RPN Program for y = ax² + bx +c "
Debug "--------------------------------------------------"
Dim MyRpnProg.TRPN_PROG_ELEMENT(0)
CreateParableProg(MyRpnProg(), @x, @a, @b, @c)
a = 1 : b=1 : c = 0 ; -> y = x² + x
For I = 1 To 10
x=I
y = RunRpnProg(MyRpnProg(), 99)
Debug "*******"
Debug "y = " + StrD(y)
Debug "*******"
Next
CompilerEndIf