This is the full implementation of RPN. It includes the functions
SQR, COS, SIN, TAN, LOG (the others can be added very easily).
I have used the optimized function of Paul Squires in order to implement
a stack using a linked list. However, it is only a subset of Paul's work
included in this.
So, have fun if you think of using this.
It will compile out of the box with PB/CC 4.0
Cheers
Steven
Code:
#Dim All #Debug Error On '#include "d:\appdev\pwr\pbcc40\winapi\win32api.inc" '#Include "pbmem.inc" %DLL_PROCESS_DETACH = 0 %DLL_PROCESS_ATTACH = 1 %DLL_THREAD_ATTACH = 2 %DLL_THREAD_DETACH = 3 %HEAP_GENERATE_EXCEPTIONS = &H00000004 %HEAP_NO_SERIALIZE = &H00000001 %HEAP_REALLOC_IN_PLACE_ONLY = &H00000010 %HEAP_ZERO_MEMORY = &H00000008 %STATUS_ACCESS_VIOLATION = &HC0000005??? %STATUS_NO_MEMORY = &HC0000017??? %FORMAT_MESSAGE_ALLOCATE_BUFFER = &H100 %FORMAT_MESSAGE_IGNORE_INSERTS = &H200 %FORMAT_MESSAGE_FROM_STRING = &H400 %FORMAT_MESSAGE_FROM_HMODULE = &H800 %FORMAT_MESSAGE_FROM_SYSTEM = &H1000 %FORMAT_MESSAGE_ARGUMENT_ARRAY = &H2000 %FORMAT_MESSAGE_MAX_WIDTH_MASK = &HFF %LANG_NEUTRAL = &H00 %SUBLANG_DEFAULT = &H01 ' user default %TRUE = 1 %FALSE = 0 %Null = 0 ' ====================================================================================================== Type PROGRAM_SECTION bDimAll As Byte bDebugError As Byte pRPN As Dword ' Reverse Polish Notation pRPNPostFix As Dword ' Reverse Polish Notation Postfix End Type ' ====================================================================================================== Type RPN_STACK ' Reverse Polish Notation Stack pOperand As Dword nLevel As Integer End Type ' ====================================================================================================== Type RPN_EVALUATOR dValue As Double End Type ' ====================================================================================================== ' Double linked list structure. Can be used as either a list head, or ' as link words. TYPE LIST_ENTRY Flink AS LIST_ENTRY PTR Blink AS LIST_ENTRY PTR END TYPE ' ====================================================================================================== TYPE RTL_CRITICAL_SECTION_DEBUG wType AS WORD CreatorBackTraceIndex AS WORD CriticalSection AS DWORD ' actually, CRITICAL_SECTION PTR, but we can't do a circular reference ProcessLocksList AS LIST_ENTRY EntryCount AS DWORD ContentionCount AS DWORD Spare(0 TO 1) AS DWORD END TYPE TYPE CRITICAL_SECTION DebugInfo AS RTL_CRITICAL_SECTION_DEBUG PTR ' The following three fields control entering and exiting the critical ' section for the resource LockCount AS LONG RecursionCount AS LONG OwningThread AS DWORD ' from the thread's ClientId->UniqueThread LockSemaphore AS DWORD SpinCount AS DWORD ' force size on 64-bit systems when packed END TYPE Type LINKED_LIST_TYPE pNext As LINKED_LIST_TYPE Ptr pPrev As LINKED_LIST_TYPE Ptr zKey As Asciiz Ptr zData As Long nType As Integer ' 0 = Long, 1 = String End Type Type LINKED_LIST_HEADER_TYPE pFirst As LINKED_LIST_TYPE Ptr pLast As LINKED_LIST_TYPE Ptr UseStrings As Byte ' TRUE if zData is pointer to a string SortList As Byte ' TRUE if zData is to maintained in sorted order Count As Long End Type %LINKED_LIST_INSERT_START = 1 %LINKED_LIST_INSERT_END = 2 %LINKED_LIST_INSERT_BEFORE = 3 %LINKED_LIST_INSERT_AFTER = 4 DECLARE SUB EnterCriticalSection LIB "KERNEL32.DLL" ALIAS "EnterCriticalSection" (lpCriticalSection AS CRITICAL_SECTION) DECLARE SUB DeleteCriticalSection LIB "KERNEL32.DLL" ALIAS "DeleteCriticalSection" (lpCriticalSection AS CRITICAL_SECTION) DECLARE SUB LeaveCriticalSection LIB "KERNEL32.DLL" ALIAS "LeaveCriticalSection" (lpCriticalSection AS CRITICAL_SECTION) DECLARE SUB InitializeCriticalSection LIB "KERNEL32.DLL" ALIAS "InitializeCriticalSection" (lpCriticalSection AS CRITICAL_SECTION) Declare Function GetProcessHeap Lib "KERNEL32.DLL" Alias "GetProcessHeap" () As Long Declare Function HeapAlloc Lib "KERNEL32.DLL" Alias "HeapAlloc" (ByVal hHeap As Dword, ByVal dwFlags As Dword, ByVal dwBytes As Dword) As Dword Declare Function HeapCompact Lib "KERNEL32.DLL" Alias "HeapCompact" (ByVal hHeap As Dword, ByVal dwFlags As Dword) As Dword Declare Function HeapCreate Lib "KERNEL32.DLL" Alias "HeapCreate" (ByVal flOptions As Dword, ByVal dwInitialSize As Dword, ByVal dwMaximumSize As Dword) As Dword Declare Function HeapDestroy Lib "KERNEL32.DLL" Alias "HeapDestroy" (ByVal hHeap As Dword) As Long Declare Function HeapFree Lib "KERNEL32.DLL" Alias "HeapFree" (ByVal hHeap As Dword, ByVal dwFlags As Dword, ByVal lpMem As Dword) As Long Declare Function HeapLock Lib "KERNEL32.DLL" Alias "HeapLock" (ByVal hHeap As Dword) As Long Declare Function HeapReAlloc Lib "KERNEL32.DLL" Alias "HeapReAlloc" (ByVal hHeap As Dword, ByVal dwFlags As Dword, ByVal lpMem As Dword, ByVal dwBytes As Dword) As Dword Declare Function HeapSize Lib "KERNEL32.DLL" Alias "HeapSize" (ByVal hHeap As Dword, ByVal dwFlags As Dword, ByVal lpMem As Dword) As Dword Declare Function HeapValidate Lib "KERNEL32.DLL" Alias "HeapValidate" (ByVal hHeap As Dword, ByVal dwFlags As Dword, lpMem As Any) As Long Declare Function FormatMessage Lib "KERNEL32.DLL" Alias "FormatMessageA" (ByVal dwFlags As Dword, lpSource As Any, ByVal dwMessageId As Dword, ByVal dwLanguageId As Dword, lpBuffer As Asciiz, ByVal nSize As Dword, Arguments As Any) As Dword Declare Function LocalFree Lib "KERNEL32.DLL" Alias "LocalFree" (ByVal hMem As Dword) As Long Declare Function GetLastError Lib "KERNEL32.DLL" Alias "GetLastError" () As Long Declare Function memWriteDebugFile(ByVal outText As String) As Long Declare Function WinErrorMsg Alias "WinErrorMsgA" (ByVal Derror As Dword) As String DECLARE FUNCTION lstrcmp LIB "KERNEL32.DLL" ALIAS "lstrcmpA" (lpString1 AS ASCIIZ, lpString2 AS ASCIIZ) AS LONG DECLARE FUNCTION lstrcmpi LIB "KERNEL32.DLL" ALIAS "lstrcmpiA" (lpString1 AS ASCIIZ, lpString2 AS ASCIIZ) AS LONG ' ====================================================================================================== Global g_CS As CRITICAL_SECTION Global g_Find_CS As CRITICAL_SECTION Global g_Delete_CS As CRITICAL_SECTION Global g_Init As Integer ' ====================================================================================================== Global PS As PROGRAM_SECTION '====================================================================================================== ' '====================================================================================================== Function MAKELANGID (ByVal P As Word, ByVal S As Word) Export As Dword Local Dresult As Dword Dresult = S Shift Left Dresult, 10 Dresult = Dresult Or P Function = Dresult End Function ' ====================================================================================================== ' '====================================================================================================== Function WinErrorMsg Alias "WinErrorMsgA" (ByVal Derror As Dword) Export As String Local Pbuffer As Asciiz Ptr Local Ncbbuffer As Dword Ncbbuffer = FormatMessage( _ %FORMAT_MESSAGE_ALLOCATE_BUFFER _ Or %FORMAT_MESSAGE_FROM_SYSTEM _ Or %FORMAT_MESSAGE_IGNORE_INSERTS, _ ByVal %Null, _ Derror, _ ByVal MAKELANGID(%LANG_NEUTRAL, %SUBLANG_DEFAULT), _ ByVal VarPtr(Pbuffer), _ 0, _ ByVal %Null) If Ncbbuffer Then Function = Peek$(Pbuffer, Ncbbuffer) LocalFree Pbuffer End If End Function ' ====================================================================================================== ' memHeapFreeMem ' ====================================================================================================== Function memHeapFreeMem(ByVal MemPtr As Dword) Export As Dword Local lResult As Long If MemPtr > 0 Then lResult = HeapFree(GetProcessHeap, %Null, ByVal MemPtr) If lResult = 0 Then Stdout FuncName$ & "-> " & WinErrorMsg(GetLastError) End If Function = lResult End If End Function ' ====================================================================================================== ' memHeapAlloc ' ====================================================================================================== Function memHeapAlloc(ByVal Size As Dword) Export As Dword Local lHeapPtr As Dword Local Retval As Dword On Error GoTo ErrorHandler If Size = 0 Then Function = 0 Exit Function End If 'If Size < 4 Then MsgBox FuncName$ & " ->" & Format$(Size) If Size < 4 Then Size = Size + (4 - Size) RetVal = HeapAlloc(GetProcessHeap,%HEAP_GENERATE_EXCEPTIONS Or %HEAP_ZERO_MEMORY, Size) 'RetVal = HeapAlloc(GetProcessHeap,%HEAP_GENERATE_EXCEPTIONS, Size) Select Case RetVal Case %STATUS_ACCESS_VIOLATION Stdout FuncName$ & ": " & "Access Violation !" Function = 0 Case %STATUS_NO_MEMORY Stdout FuncName$ & ": " & "No memory !" Function = 0 Case Else Function = RetVal End Select Exit Function ErrorHandler: Stdout FuncName$ & ": " & Error$(Err) End Function ' ====================================================================================================== ' memHeapAllocString ' ====================================================================================================== Function memHeapAllocString(ByVal sData As String) Export as Dword Local sPtr As String Ptr On Error GoTo ErrorHandler sPtr = memHeapAlloc(Len(sData)) If sPtr > 0 Then @sPtr = sData Function = sPtr Else ' MsgBox "Error in function " & FuncName$ & ": sptr = 0" Function = 0 End If Exit Function ErrorHandler: Stdout FuncName$ & ": " & Error$(Err) End Function ' ====================================================================================================== ' memHeapRetrieveString ' ====================================================================================================== Function memHeapRetrieveString Alias "memHeapRetrieveString" ( ByVal UDTString As String Ptr ) Export As String On Error GoTo ErrorHandler Local ptrData As String Ptr Local ptrVal As Dword Local retVal As Long Local DataStr As String If UDTString = 0 Then Function = "" Exit Function End If Try ' ======================================================================================================== ' It seems that the HeapValidate function only works when the HeapAlloc flag has been set to ' %HEAP_GENERATE_EXCEPTIONS Or %HEAP_ZERO_MEMORY or the HeapReAlloc flag has been set to ' %HEAP_GENERATE_EXCEPTIONS Or %HEAP_REALLOC_IN_PLACE_ONLY Or %HEAP_ZERO_MEMORY ' ======================================================================================================== retval = HeapValidate(GetProcessHeap(), %Null, ByVal UDTString) If retVal = 0 Then Stdout "Error validating Heap for address " & Format$(udtString) & ". Func: " & FuncName$ 'Function = "" Exit Function End If Catch Exit Try End Try Function = @UDTSTring ' Function = DataStr Exit Function ErrorHandler: Stdout "Error in " & FuncName$ & ": " & Error$(Err) End Function ' ====================================================================================================== Function List_Initialize() Export As Long g_Init = %True InitializeCriticalSection g_CS InitializeCriticalSection g_Find_CS InitializeCriticalSection g_Delete_CS End Function ' ====================================================================================================== ' ' ====================================================================================================== Function List_Uninitialize() Export As Long DeleteCriticalSection g_CS DeleteCriticalSection g_Find_CS DeleteCriticalSection g_Delete_CS End Function Function MemAlloc( ByVal nSize As Long ) Export As Dword If nSize > 0 Then Function = HeapAlloc( GetProcessHeap(), %HEAP_ZERO_MEMORY, nSize) End If End Function '// '// Free memory located at a specified location '// Function MemFree( ByRef pMem As Dword ) Export As Long If pMem Then Function = HeapFree( GetProcessHeap(), 0, ByVal pMem) pMem = 0 End If End Function '// '// List_GetByPosition '// Returns the pointer to the item in the linked list at the specified position '// Function List_GetByPosition( ByVal pListAddress As Dword, _ ByVal nPosition As Long _ ) Export As Dword Local pList As LINKED_LIST_HEADER_TYPE Ptr Local pListItem As LINKED_LIST_TYPE Ptr Local x As Long pList = pListAddress If pList = 0 Then Exit Function EnterCriticalSection g_CS ' Iterate to the specified ordinal position pListItem = @pList.pFirst x = 1 Do Until x >= nPosition pListItem = @pListItem.pNext If pListItem = 0 Then Exit Do Incr x Loop Function = pListItem LeaveCriticalSection g_CS End Function '// '// Sub that will assign a string to a memory location '// and free any memory that may have already been allocated. '// pMem must be ByRef. '// Sub MemString( ByRef pMem As Dword, _ ByVal sData As String ) If pMem Then HeapFree( GetProcessHeap(), ByVal 0, ByVal pMem) ' free any existing memory sData = sData & $Nul pMem = HeapAlloc( GetProcessHeap(), %HEAP_ZERO_MEMORY, Len( sData ) + 1) If pMem Then Poke$ pMem, sData End Sub '// Handles all of the different ways of Adding an item to the linked list. '// Returns a pointer to the added item. '// Function List_Add_Internal( ByVal pListAddress As Dword, _ ByVal pListItemAddress As Dword, _ ByVal sKey As String, _ ByVal zData As Long, _ ByVal nAddMethod As Long, _ ByVal nDataType As Integer) Export As Dword Local pList As LINKED_LIST_HEADER_TYPE Ptr Local pNewItem As LINKED_LIST_TYPE Ptr Local pListItem As LINKED_LIST_TYPE Ptr Local zPtr1 As Asciiz Ptr Local zPtr2 As Asciiz Ptr Local nFirstElement As Long Local nLastElement As Long Local nPos As Long Local nResult As Long EnterCriticalSection g_CS pList = pListAddress If pList = 0 Then LeaveCriticalSection g_CS Exit Function End If sKey = lcase$(sKey) pListItem = pListItemAddress ' Create memory for the new node to occupy pNewItem = MemAlloc( SizeOf(LINKED_LIST_TYPE) ) If pNewItem = 0 Then Function = 0 LeaveCriticalSection g_CS Exit Function End If ' Assign the Data. The assignment depends on whether the ' data being added is a string or a numeric. If @pList.UseStrings Then zPtr1 = zData MemString @pNewItem.zData, @zPtr1 Else @pNewItem.zData = zData End If @pNewItem.nType = nDataType ' Assign the Key MemString @pNewItem.zKey, sKey ' Determine how this key will be added to the List. The fastest ' way is to simply add it to the end of the list. If the user ' wants the list in sorted order then we must find where in the ' list the new item should be placed. ' We will use a binary search type of algoritm because the list ' should already be in sorted. This is a little more complicated ' then a normal linear search but the performance improvement is ' substantial - normally 200% to 300% (especially for large lists). If @pList.SortList Then nFirstElement = 1 nLastElement = @pList.Count Do nPos = (nFirstElement + nLastElement) \ 2 pListItem = List_GetByPosition( pList, nPos ) If pListItem = 0 Then Exit Do If @pList.UseStrings Then ' Do string comparisons (case sensitive) zPtr1 = @pListItem.zData zPtr2 = zData nResult = lstrcmp( @zPtr1, @zPtr2 ) Else ' Do a simple numeric comparison If @pListItem.zData = zData Then nResult = 0 If @pListItem.zData < zData Then nResult = -1 If @pListItem.zData > zData Then nResult = 1 End If Select Case nResult Case 0 Exit Do ' exact match Case > 0 nLastElement = nPos - 1 Case < 0 nFirstElement = nPos + 1 End Select Loop While nLastElement >= nFirstElement If pListItem = 0 Then nAddMethod = %LINKED_LIST_INSERT_END Else Select Case nResult Case >= 0 nAddMethod = %LINKED_LIST_INSERT_BEFORE Case Else nAddMethod = %LINKED_LIST_INSERT_AFTER End Select End If End If Select Case nAddMethod Case %LINKED_LIST_INSERT_START ' Add the new item at the start of the list If @pList.pLast = 0 Then @pList.pFirst = pNewItem @pList.pLast = pNewItem Else @pNewItem.pNext = @pList.pFirst If @pList.pFirst Then @[email protected] = pNewItem End If @pList.pFirst = pNewItem End If Case %LINKED_LIST_INSERT_END '- Add this item to the end of the list. We save the link to ' the last item in pList in order to speed up adding future ' items otherwise we need to iterate the entire list for every ' add in order to find the last item. If @pList.pLast = 0 Then @pList.pFirst = pNewItem Else If @pList.pLast Then @[email protected] = pNewItem End If @pNewItem.pPrev = @pList.pLast End If @pList.pLast = pNewItem Case %LINKED_LIST_INSERT_BEFORE If @pListItem.pPrev = 0 Then ' Start of list If @pList.pFirst Then @[email protected] = pNewItem End If @pList.pFirst = pNewItem @pNewItem.pNext = pListItem Else @pNewItem.pPrev = @pListItem.pPrev @pNewItem.pNext = pListItem @pListItem.pPrev = pNewItem @[email protected] = pNewItem End If Case %LINKED_LIST_INSERT_AFTER If @pListItem.pNext = 0 Then ' last item in list @pListItem.pNext = pNewItem @pNewItem.pPrev = pListItem @pList.pLast = pNewItem Else @[email protected] = pNewItem @pNewItem.pNext = @pListItem.pNext @pNewItem.pPrev = pListItem @pListItem.pNext = pNewItem End If End Select Incr @pList.Count Function = pNewItem LeaveCriticalSection g_CS End Function ' ====================================================================================================== Function List_AddStr( ByVal pListAddress As Dword, _ ByVal sKey As String, _ ByVal zData As String _ ) Export As Dword Local pData As Long pData = memHeapAllocString(zData) Function = List_Add_Internal( pListAddress, 0, sKey, pData, %LINKED_LIST_INSERT_END, 1) End Function '// '// List_Delete '// Removes a node from the list and frees its memory '// Function List_Delete( ByVal pListAddress As Dword, _ ByVal pListItemAddress As Dword _ ) Export As Long Local pList As LINKED_LIST_HEADER_TYPE Ptr Local pDelete As LINKED_LIST_TYPE Ptr EnterCriticalSection g_Delete_CS pList = pListAddress If pList = 0 Then LeaveCriticalSection g_Delete_CS Exit Function End If pDelete = pListItemAddress If pDelete = 0 Then LeaveCriticalSection g_Delete_CS Exit Function End If ' If pLast points to the node being deleted then ' we need to adjust that to point to the previous node. If @pList.pLast = pDelete Then @pList.pLast = @pDelete.pPrev End If '- Remove the item from the list If @pDelete.pPrev = 0 Then @pList.pFirst = @pDelete.pNext If @pDelete.pNext Then @[email protected] = 0 End If Else @[email protected] = @pDelete.pNext If @pDelete.pNext Then If @pDelete.pNext Then @[email protected] = @pDelete.pPrev End If End If End If '- Free pDelete (and any allocated string memory) MemFree @pDelete.zKey If @pList.UseStrings Then MemFree @pDelete.zData Else If @pDelete.nType = 1 Then MemFree @pDelete.zData Else @pDelete.zData = 0 End If End If MemFree pDelete Decr @pList.Count LeaveCriticalSection g_Delete_CS Function = %TRUE End Function ' ====================================================================================================== Function List_DeleteByPosition( ByVal pListAddress As Dword, _ ByVal nPosition As Long) Export As Dword Local pList As LINKED_LIST_HEADER_TYPE Ptr Local pListItem As LINKED_LIST_TYPE Ptr Local x As Long pList = pListAddress If pList = 0 Then Exit Function ' Iterate to the specified ordinal position pListItem = @pList.pFirst x = 1 Do Until x >= nPosition pListItem = @pListItem.pNext If pListItem = 0 Then Exit Do Incr x Loop Function = List_Delete(pListAddress, pListItem) End Function ' ====================================================================================================== Function List_FindByKeyString( ByVal pListAddress As Dword, _ ByVal zKey As String _ ) Export As String Local pList As LINKED_LIST_HEADER_TYPE Ptr Local pNext As LINKED_LIST_TYPE Ptr pList = pListAddress If pList = 0 Then Exit Function EnterCriticalSection g_CS pNext = @pList.pFirst Do Until pNext = 0 If @pNext.zKey Then If Lstrcmpi( @[email protected], ByCopy zKey ) = 0 Then If @pNext.zData Then Function = memHeapRetrieveString(@pNext.zData) End If Exit Do End If End If pNext = @pNext.pNext Loop LeaveCriticalSection g_CS End Function '// '// Retrieve the number of items in the linked list table '// Function List_GetCount( ByVal pListAddress As Dword ) Export As Long Local pList As LINKED_LIST_HEADER_TYPE Ptr pList = pListAddress If pList = 0 Then Exit Function Function = @pList.Count End Function '// '// List_Create '// Returns a pointer to a newly created linked list '// Function List_Create( ByVal nUseStrings As Long, _ ByVal nSortList As Long _ ) Export As Dword If g_Init = %False Then Function = 1 ' Not initialized. End If EnterCriticalSection g_CS Local pList As LINKED_LIST_HEADER_TYPE Ptr pList = MemAlloc( SizeOf(LINKED_LIST_HEADER_TYPE) ) If pList Then ' The data in the Linked List items can represent either a ' string or a simple numeric value. If you want the list to ' hold strings for the Data then set nUseStrings = TRUE @pList.UseStrings = nUseStrings ' Set flag to determine whether the zData element is maintained ' in a sorted order. @pList.SortList = nSortList Function = pList End If LeaveCriticalSection g_CS End Function ' ====================================================================================================== ' ' ====================================================================================================== Function List_CreateStandard() Export As Dword Function = List_Create(%False, %False) End Function ' ====================================================================================================== ' IsNumeric (With decimal point." ' ====================================================================================================== Function IsNumeric(ByVal InText As String) As Byte Local n As Long For n = 1 To Len(inText) If Instr("0123456789.", Mid$(InText, n, 1)) Then Function = 1 Else Function = 0 Exit Function End If Next End Function ' ====================================================================================================== ' ' ====================================================================================================== Function PopRPN(ByRef Operand As String, ByRef Level As Integer) As Byte Local Buffer As String Local Count As Long Local tRPN As RPN_STACK ' ====================================================================================================== Count = List_GetCount(PS.pRPN) If Count Then Buffer = List_FindByKeyString(PS.pRPN, Format$(Count)) If Len(Buffer) Then Lset tRPN = Buffer If tRPN.pOperand Then Operand = memHeapRetrieveString(tRPN.pOperand) Call memHeapFreeMem(tRPN.pOperand) End If Level = tRPN.nLevel Call List_DeleteByPosition(PS.pRPN, Count) Function = 1 End If Else Function = 0 End If End Function ' ====================================================================================================== ' ' ====================================================================================================== Function GetRPN(ByRef tRPN As RPN_STACK, ByVal nItem As Long) As Byte Local Buffer As String Local Count As Long Count = List_GetCount(PS.pRPN) If Count And nItem > 0 And nItem <= Count Then Buffer = List_FindByKeyString(PS.pRPN, Format$(nItem)) If Len(Buffer) Then Lset tRPN = Buffer End If Function = 1 End If End Function ' ====================================================================================================== ' ' ====================================================================================================== Function PshRPN(ByVal tRPN As RPN_STACK) As Byte Local Count As Long Local sData As String Count = List_GetCount(PS.pRPN) Incr Count If tRPN.pOperand Then sData = memHeapRetrieveString(tRPN.pOperand) End iF Call List_AddStr(PS.pRPN, Format$(Count), (tRPN)) End Function ' ====================================================================================================== ' ' ====================================================================================================== Function PopRPNEvaluator() As Double Local Buffer As String Local Count As Long Local tRPN As RPN_EVALUATOR ' ====================================================================================================== Count = List_GetCount(PS.pRPNPostFix) If Count Then Buffer = List_FindByKeyString(PS.pRPNPostFix, Format$(Count)) If Len(Buffer) Then Lset tRPN = Buffer Function = tRPN.dValue Call List_DeleteByPosition(PS.pRPNPostFix, Count) End If Else Function = 0 End If End Function ' ====================================================================================================== ' ' ====================================================================================================== Function PshRPNEvaluator(ByVal dValue As Double) As Byte Local Count As Long Local sData As String Local tRPN As RPN_EVALUATOR Count = List_GetCount(PS.pRPNPostFix) Incr Count tRPN.dValue = dValue Call List_AddStr(PS.pRPNPostFix, Format$(Count), (tRPN)) End Function ' ====================================================================================================== Function ParseTokenSeries(ByVal sTokenSeries As String, ByRef Tokens() As String) As Long Local Cnt1 As Long Local n As Long Local nToken As Long Local strPos As Long Local endPos As Long Local sTxt As String Local sToken As String Local bFound As Integer Local Buffer As String bFound = %False strPos = 1 Buffer = "" For n = 1 To Len(sTokenSeries) sToken = Mid$(sTokenSeries, n, 1) Select Case sToken Case "=", "+", "-", "/", "\", "*", "@", "(", ")", "^", "%", "&", "?", "$", ",", "'", "<", ">", "#", Chr$(34) bFound = %True If len(sTxt) Then Buffer = Buffer & sTxt & "|" End If Buffer = Buffer & sToken If n < Len(sTokenSeries) Then Buffer = Buffer & "|" End If sTxt = "" StrPos = n + 1 'Case Chr$(34) Case Else sTxt = sTxt & Mid$(sTokenSeries, n, 1) End Select Next If BFound = %True And Len(sTxt) Then Buffer = Buffer & sTxt End If If Len(Buffer) Then Cnt1 = ParseCount(Buffer, "|") ReDim Tokens(1 To Cnt1) As String Parse Buffer, Tokens(), "|" End If Function = Cnt1 End Function ' ====================================================================================================== Function Parser(ByVal InLine As String, ByRef aArray() As String) As Long Local Length As Long Local n As Long Local Char As String Local Buffer As String Local wQuote As Word Local wIndex As Word ReDim aArray(0) As String '#TEST Print "'" & InLine & "'" '#TEST WaitKey$ Length = Len(InLine) wIndex = 1 wQuote = 0 n = 1 Do Char = Mid$(InLine, n, 1) If Char = Chr$(34) Then Incr wQuote End If Select Case wQuote Case 0 If Char <> " " Then Buffer = Buffer & Char End If Select Case Char Case " ", "<", ">" If Buffer <> "" Then ReDim Preserve aArray(0 To wIndex) As String aArray(wIndex) = Buffer Buffer = "" Incr wIndex End If End Select Case 1 Buffer = Buffer & Char Case 2 Buffer = Buffer & Char ReDim Preserve aArray(0 to wIndex) As String aArray(wIndex) = Buffer Buffer = "" If n < Length Then Incr wIndex End If wQuote = 0 End Select Incr n Loop until n > Length If Buffer <> "" Then ReDim Preserve aArray(0 to wIndex) As String aArray(wIndex) = Buffer End If Function = wIndex End Function ' ====================================================================================================== ' ' ====================================================================================================== Function ParseLine(ByVal InLine As String, ByRef TargetArray() As String, ByRef bResult As Integer) As Long Local Cnt1 As Long Local Cnt2 As Long Local n As Long Local k As Long Local Token As String Local TokenSeries As String Local Elements As Dword Dim Tokens(0) As String Cnt1 = ParseCount(InLine, " ") Dim pArray1(1 To Cnt1) As String ReDim TargetArray(0) As String ' ====================================================================================================== ' Replace Parse command with our own parser so that spaces in Quotes are NOT discarded. ' ====================================================================================================== ' Parse InLine, pArray1(), " " Cnt1 = Parser(InLine, pArray1()) For n = 1 To Cnt1 Token = pArray1(n) '#TEST Print "1. Token = " & Token If Instr("=+-/\*@()^%&?$,<>#", Token) = 0 Then bResult = ParseTokenSeries(Token, Tokens()) If bResult Then ReDim Preserve TargetArray(Elements + bResult) As String For k = 1 To bResult TargetArray(Elements + k) = Tokens(k) Next Elements = Elements + bResult End If If bResult = 0 Then Incr Elements ReDim Preserve TargetArray(Elements) As String TargetArray(Elements) = Token End If Else Incr Elements ReDim Preserve TargetArray(Elements) As String TargetArray(Elements) = Token End If Next Function = Elements End Function ' ====================================================================================================== ' ' ====================================================================================================== Function MathExpressionNotation(ByRef Tokens() As String) As String Local n As Long Local TokenCount As Dword Local Token As String Local wIndex As Word Local Buffer As String Local tRPN As RPN_STACK Local CurLevel As Integer Local Level As Integer Local Operand As String Dim Tokens(0) As String Dim Finals(0) As String TokenCount = Ubound(Tokens) ReDim Finals(1 To TokenCount) As String wIndex = 0 For n = 0 To TokenCount Token = Ucase$(Tokens(n)) Select Case Token Case "+", "-" Do While PopRPN(Operand, Level) If tRPN.nLevel = CurLevel Then Incr wIndex Finals(wIndex) = Operand Else tRPN.pOperand = memHeapAllocString(Operand) tRPN.nLevel = Level PshRPN tRPN Exit Do End If Loop tRPN.pOperand = memHeapAllocString(Ucase$(Token)) tRPN.nLevel = CurLevel PshRPN tRPN Case "*", "/" Do While PopRPN(Operand, Level) If Level = CurLevel Then Select Case Operand CAse "^", "SQR", "COS", "SIN", "TAN", "EXP", "LOG" Incr wIndex Finals(wIndex) = Operand Case Else tRPN.pOperand = memHeapAllocString(Operand) tRPN.nLevel = Level PshRPN tRPN Exit Do End Select Else tRPN.pOperand = memHeapAllocString(Operand) tRPN.nLevel = Level PshRPN tRPN Exit Do End If Loop tRPN.pOperand = memHeapAllocString(Ucase$(Token)) tRPN.nLevel = CurLevel Call PshRPN (tRPN) Case "^" tRPN.pOperand = memHeapAllocString(Ucase$(Token)) tRPN.nLevel = CurLevel PshRPN tRPN Case "COS", "SIN", "SQR", "TAN", "LOG" tRPN.pOperand = memHeapAllocString(Ucase$(Token)) tRPN.nLevel = CurLevel PshRPN tRPN Case "AND" tRPN.pOperand = memHeapAllocString(Ucase$(Token)) tRPN.nLevel = CurLevel PshRPN tRPN Case "(" Incr CurLevel Case ")" Do While PopRPN(Operand, Level) If Level = CurLevel Then Incr wIndex Finals(wIndex) = Operand Else tRPN.pOperand = memHeapAllocString(Operand) tRPN.nLevel = Level PshRPN tRPN Exit Do End If Loop Decr CurLevel Case "=" Case Else If IsNumeric(Token) Then Incr wIndex Finals(wIndex) = Token End If 'Buffer = Buffer & Ucase$(Trim$(Token)) & " " 'tRPN.pOperand = memHeapAllocString(Token) 'tRPN.nLevel = CurLevel 'PshRPN tRPN End Select Next TokenCount = List_GetCount(PS.pRPN) Do While PopRPN(Operand, Level) Incr wIndex Finals(wIndex) = Operand Loop For n = 1 To wIndex Buffer = Buffer & Finals(n) If n < wIndex Then Buffer = Buffer & " " End If Next Function = Buffer ' Print EvaluatePostFix(Buffer) End Function ' ====================================================================================================== ' ' ====================================================================================================== Function EvaluatePostfix(ByVal Expression As String) As Double Local i As Integer Local A As Double Local C As Double Local D As Double Dim Expn(0 To ParseCount(Expression, " ") - 1) As String Parse Expression, Expn(), " " For i = 0 To UBound(Expn()) Select Case Expn(i) Case "+" C = PopRPNEvaluator() D = PopRPNEvaluator() A = D + C Call PshRPNEvaluator(A) Case "-" C = PopRPNEvaluator() D = PopRPNEvaluator() A = D - C Call PshRPNEvaluator(A) Case "*" C = PopRPNEvaluator() D = PopRPNEvaluator() A = D * C Call PshRPNEvaluator(A) Case "/", "\" C = PopRPNEvaluator() D = PopRPNEvaluator() A = D / C Call PshRPNEvaluator(A) Case "^" C = PopRPNEvaluator() D = PopRPNEvaluator() A = D ^ C Call PshRPNEvaluator(A) Case "SIN" C = PopRPNEvaluator() A = Sin(C) Call PshRPNEvaluator(A) Case "COS" C = PopRPNEvaluator() A = Cos(C) Call PshRPNEvaluator(A) Case "TAN" C = PopRPNEvaluator() A = Tan(C) Call PshRPNEvaluator(A) Case "SQR" C = PopRPNEvaluator() A = SQR(C) Call PshRPNEvaluator(A) Case "LOG" C = PopRPNEvaluator() A = LOG(C) Call PshRPNEvaluator(A) Case "AND" C = PopRPNEvaluator() D = PopRPNEvaluator() A = D AND C Call PshRPNEvaluator(A) Case Else A = Val(Expn(i)) Call PshRPNEvaluator(A) End Select Next Function = A End Function ' ====================================================================================================== ' ' ====================================================================================================== Function RPN(ByVal Buffer As String) As Long Local Elements As Long Local n As Long Local bResult As Integer Local RP As String ' ====================================================================================================== Dim TargetArray(0) As String Elements = ParseLine(Buffer, TargetArray(), bResult) ' ====================================================================================================== RP = MathExpressionNotation(TargetArray()) ' ====================================================================================================== Print "RPN = " & RP Print EvaluatePostFix(RP) End Function Function pbMain() As Long Call List_Initialize() PS.pRPN = List_CreateStandard() PS.pRPNPostFix = List_CreateStandard() Call RPN("(5 + 8 * sin(2 * 15)) / (2 + tan(45))") End Function