home *** CD-ROM | disk | FTP | other *** search
/ Planet Source Code Jumbo …e CD Visual Basic 1 to 7 / 4_2005-2006.ISO / data / Zips / RPN_Demo192022822005.psc / ModRPN.bas < prev    next >
BASIC Source File  |  2005-08-02  |  20KB  |  526 lines

  1. Attribute VB_Name = "ModRPN"
  2. Option Explicit
  3.  
  4. Public glParsedSize              As Long
  5. Public gstrTokens(1 To 7)        As String
  6. Public gstrParsed(0 To 200)      As String
  7. Public gstrDoubleQuote           As String * 1
  8.  
  9. '**************************************
  10. ' Name: CalcRPN
  11. ' Description: This function calculates a results from a RPN formula.
  12. ' By: Juha Mensola
  13. '
  14. ' Inputs: The formula as string, separated by spaces.
  15. '
  16. ' Returns: The end result as double
  17. '
  18. 'Assumes: For those unfamiliar with RPN,
  19. '     it is a notation somewhat different from
  20. '     the standard way of writing down formulas.
  21. '     For example the calculation (2+3)*(4+5)(= 45)
  22. '     in RPN would be 2 3 + 4 5 + *.
  23. 'Also, this function understands multiple operands in the following manner:
  24. 'Formula 5*5+1*2*3/4-1 (= 25.5) would be 5 5 * 1 2 3 * * 4 / 1 - +
  25. 'The RemoveCell-function is used by the CalcRPN-function and should also be included in your project.
  26. 'The function currently understands the following perators: +, -, *, / and \(integer divide).
  27. 'But you can add new ones easily. Just add another case-statement and so on.
  28. '
  29. 'Side Effects: None known.
  30. 'This code is copyrighted and has limited warranties.
  31. 'Please see http://www.planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=24179&lngWId=1
  32. 'for details.
  33. '**************************************
  34. 'Converting from infix to postfix notation
  35. 'The algorithm to convert from normal, infix, notation to postfix notation works as follows,
  36. ' converting an expression on the input stream into an equivalent one on the output stream.
  37. ' The expression on the input stream is examined symbol by symbol:
  38. '
  39. '
  40. ' Identifiers and constants are passed directly to the output stream, but operators and
  41. '   parentheses require special attention.
  42. ' An opening parenthesis, '(', is simply placed on the operator stack.
  43. ' A closing parenthesis, ')', causes all of the operators on the stack down
  44. '   to the matching '(' to be placed on the output stream, the operators being
  45. '   removed from the top, down. The matching '(' is not placed on the output
  46. '   stream, and neither is the ')' -- these are discarded.
  47. ' An operator on the input stream causes one of two actions to be taken:
  48. '
  49. '   If the stack is empty, or there is a '(' on the top of it, then the
  50. '     operator is placed on the top of the stack.
  51. '   If the top of the stack is an operator then if the current operator
  52. '     has a higher priority value than the one on the top, again, it is placed on the top of the stack.
  53. '   If, however, the operator has a priority which is lower than or equal
  54. '     to that of the operator on the top of the stack (again assuming all
  55. '     operators are left-associative), then the operator at the top of the stack is moved to the ouput stream.
  56. '   The whole of the above process is repeated until the operator can be
  57. '   placed on the top of the stack.
  58. '
  59. 'This is only a simple introduction to Reverse Polish notation --
  60. ' more can be found in most books on compiling. We will not be using
  61. ' it any further as we will use the operator precedence method to parse
  62. ' expressions and will focus on tree-walking techniques when discussing code generation.
  63. '
  64. 'article with animated compile
  65. 'http://www.spsu.edu/cs/faculty/bbrown/web_lectures/postfix/
  66. 'Converting Infix to Postfix
  67. '
  68. '
  69. 'We know that the infix expression (A+B)/(C-D) is equivalent to the postfix expression AB+CD-/.
  70. 'Let's convert the former to the latter.
  71. '
  72. 'We have to know the rules of operator precedence in order to convert infix to postfix.
  73. 'The operations + and - have the same precedence. Multiplication and division, which we will
  74. 'represent as * and / also have equal precedence, but both have higher precedence than + and -.
  75. 'These are the same rules you learned in high school.
  76. '
  77. 'We place a "terminating symbol"  after the infix expression to serve as a marker that we
  78. 'have reached the end of the expression. We also push this symbol onto the stack.
  79. '
  80. 'After that, the expression is processed according to the following rules:
  81. '
  82. 'Variables (in this case letters) are copied to the output
  83. '
  84. 'Left parentheses are always pushed onto the stack
  85. '
  86. 'When a right parenthesis is encountered, the symbol at the top of the stack is
  87. 'popped off the stack and copied to the output. Repeat until the symbol at the top
  88. 'of the stack is a left parenthesis. When that occurs, both parentheses are discarded.
  89. '
  90. 'Otherwise, if the symbol being scanned has a higher precedence than the symbol at
  91. 'the top of the stack, the symbol being scanned is pushed onto the stack and thee
  92. 'scan pointer is advanced.
  93. '
  94. 'If the precedence of the symbol being scanned is lower than or equal to the
  95. 'precedence of the symbol at the top of the stack, one element of the stack is
  96. 'popped to the output; the scan pointer is not advanced. Instead, the symbol being
  97. 'scanned will be complared with the new top element on the stack.
  98. '
  99. 'When the terminating symbol is reached on the input scan, the stack is popped to
  100. 'the output until the terminating symbol is also reached on the stack. Then the
  101. 'algorithm terminates.
  102. '
  103. 'If the top of the stack is a left parenthesis and the terminating symbol is scanned,
  104. 'or a right parenthesis is scanned when the terminating symbol is at the top of the stack,
  105. 'the parentheses of the original expression were unbalanced and an unrecoverable error has occurred.
  106. Public Function CalcRPN(pstrStatements() As String) As Double
  107.   Dim dOperand1         As Double
  108.   Dim dOperand2         As Double
  109.   Dim dResult           As Double
  110.   Dim bMultipleOperands As Boolean
  111.   Dim strOperator       As String
  112.   Dim strTmp            As String
  113.   Dim lCnt              As Long
  114.   Dim lMultipleStartPos As Long
  115.   Dim lX                As Long
  116.  
  117.   On Error GoTo ErrHandler
  118.   lCnt = LBound(pstrStatements)
  119.   Do Until False
  120. '    strTmp = vbNullString
  121. '    For lX = LBound(pstrStatements) To UBound(pstrStatements)
  122. '      strTmp = strTmp & pstrStatements(lX) & " "
  123. '    Next lX
  124. '    Debug.Print strTmp
  125.     If Not IsNumeric(pstrStatements(lCnt)) Then
  126.       lCnt = lCnt - 2
  127.     ElseIf Not IsNumeric(pstrStatements(lCnt + 1)) Then 'NOT NOT...
  128.       lCnt = lCnt - 1
  129.     ElseIf IsNumeric(pstrStatements(lCnt + 2)) Then 'NOT NOT...
  130.       bMultipleOperands = True
  131.       lMultipleStartPos = lCnt
  132.       Do Until False
  133.         If Not IsNumeric(pstrStatements(lCnt + 2)) Then Exit Do
  134.         lCnt = lCnt + 1
  135.       Loop
  136.     End If
  137.     dOperand1 = pstrStatements(lCnt)
  138.     dOperand2 = pstrStatements(lCnt + 1)
  139.     strOperator = pstrStatements(lCnt + 2)
  140.     Select Case strOperator
  141.       Case "+":   dResult = dOperand1 + dOperand2
  142.       Case "-":   dResult = dOperand1 - dOperand2
  143.       Case "*":   dResult = dOperand1 * dOperand2
  144.       Case "/":   dResult = dOperand1 / dOperand2
  145.       Case "^":   dResult = dOperand1 ^ dOperand2
  146.       Case "\":   dResult = dOperand1 \ dOperand2
  147.       Case "MOD": dResult = CLng(dOperand1) Mod CLng(dOperand2)
  148.       Case "AND": dResult = CLng(dOperand1) And CLng(dOperand2)
  149.       Case "OR":  dResult = CLng(dOperand1) Or CLng(dOperand2)
  150.       Case "XOR": dResult = CLng(dOperand1) Xor CLng(dOperand2)
  151.     End Select
  152.     If bMultipleOperands Then
  153.       pstrStatements(lCnt) = dResult
  154.       Call RemoveCell(pstrStatements, lCnt + 1, 2)
  155.       lCnt = lMultipleStartPos
  156.       bMultipleOperands = False
  157.     Else 'BMULTIPLEOPERANDS = FALSE/0
  158.       pstrStatements(lCnt) = dResult
  159.       Call RemoveCell(pstrStatements, lCnt + 1, 2)
  160.       lCnt = lCnt + 1
  161.       ' If UBound(pstrStatements) < 2 Then Exit Do
  162.     End If
  163.     If UBound(pstrStatements) < 2 Then Exit Do
  164.   Loop
  165.   CalcRPN = pstrStatements(LBound(pstrStatements))
  166. Exit Function
  167.  
  168. ErrHandler:
  169.   MsgBox "Error!"
  170.   '        Resume Next
  171. End Function
  172.  
  173. Private Function flOperPrecedence(ByVal pstrTst As String) As Long
  174.   Select Case pstrTst
  175.     Case "^":      flOperPrecedence = 8
  176.     Case "AND":    flOperPrecedence = 7
  177.     Case "OR":     flOperPrecedence = 6
  178.     Case "XOR":    flOperPrecedence = 5
  179.     Case "*", "/": flOperPrecedence = 4
  180.     Case "\":      flOperPrecedence = 3
  181.     Case "MOD":    flOperPrecedence = 2
  182.     Case "+", "-": flOperPrecedence = 1
  183.     Case "(":      flOperPrecedence = -1
  184.     Case Else:     MsgBox "Unrecognized Operator: " & pstrTst
  185.   End Select
  186. End Function
  187.  
  188. Public Sub ParsedEqn2RPNorder(pstrRPN() As String, plRPNpntr As Long)
  189.   Dim lX                 As Long
  190.   Dim lY                 As Long
  191.   Dim lParseSize         As Long
  192.   Dim lStrt              As Long
  193.   Dim lStackPntr         As Long
  194.   Dim lTstPrec           As Long
  195.   Dim lStckPrec          As Long
  196.   Dim strTst             As String
  197.   Dim strTmp             As String
  198.   Dim strTmps            As String
  199.   Dim strStack(1 To 100) As String
  200.   Dim bRepeat            As Boolean
  201.   Dim bUnaryNeg          As Boolean
  202.  
  203.   'test that there are matching parens
  204. '  On Error GoTo HaveanError
  205.   lParseSize = glParsedSize
  206.   lStrt = LBound(gstrParsed()) + 2
  207.   lStackPntr = 0
  208.   plRPNpntr = 0
  209.   bUnaryNeg = False
  210.   'ReDim pstrRPN(1) As String
  211.   For lX = lStrt To lParseSize
  212.     strTst = gstrParsed(lX)
  213. '    strTmp = ":OutStream: "
  214. '    For lY = 1 To plRPNpntr
  215. '      strTmp = strTmp & " " & Trim$(pstrRPN(lY))
  216. '    Next lY
  217. '    strTmps = ":Stack: "
  218. '    For lY = 1 To lStackPntr
  219. '      strTmps = strTmps & " " & Trim$(strStack(lY))
  220. '    Next lY
  221. '    Debug.Print ":TestOp: " & strTst
  222. '    Debug.Print strTmp
  223. '    Debug.Print strTmps
  224. '    Debug.Print
  225.     If strTst = "(" Then
  226.       'push
  227.       lStackPntr = lStackPntr + 1
  228.       strStack(lStackPntr) = strTst
  229.     ElseIf strTst = ")" Then 'NOT STRTST...
  230.       'pop
  231.       If strStack(lStackPntr) <> "(" Then
  232.         plRPNpntr = plRPNpntr + 1
  233.         ReDim Preserve pstrRPN(1 To plRPNpntr) As String
  234.         pstrRPN(plRPNpntr) = strStack(lStackPntr)
  235.       End If
  236.       lStackPntr = lStackPntr - 1
  237.       Do While strStack(lStackPntr) <> "("
  238.         'pop
  239.         plRPNpntr = plRPNpntr + 1
  240.         ReDim Preserve pstrRPN(1 To plRPNpntr) As String
  241.         pstrRPN(plRPNpntr) = strStack(lStackPntr)
  242.         lStackPntr = lStackPntr - 1
  243.         If lStackPntr = 0 Then Exit Do
  244.       Loop
  245.       lStackPntr = lStackPntr - 1
  246.     Else 'NOT STRTST...
  247.       If IsNumeric(strTst) Then
  248.         plRPNpntr = plRPNpntr + 1
  249.         ReDim Preserve pstrRPN(1 To plRPNpntr) As String
  250.         If bUnaryNeg Then
  251.           pstrRPN(plRPNpntr) = Trim$(Str$(-Val(strTst)))
  252.           bUnaryNeg = False
  253.         Else 'BUNARYNEG = FALSE/0
  254.           pstrRPN(plRPNpntr) = strTst
  255.         End If
  256.       Else 'ISNUMERIC(STRTST) = FALSE/0
  257.         bRepeat = True
  258.         Do While bRepeat
  259.           lTstPrec = flOperPrecedence(strTst)
  260.           If lStackPntr = 0 Then
  261.             If lX = lStrt Then
  262.               bUnaryNeg = True
  263.               Exit Do
  264.             Else 'NOT LX...
  265.               lStckPrec = 0
  266.             End If
  267.           Else 'NOT LSTACKPNTR...
  268.             lStckPrec = flOperPrecedence(strStack(lStackPntr))
  269.           End If
  270.           'if lStckPrec negative then unary -
  271.           If (lStckPrec < 0) Then
  272.             If strTst = "-" Then
  273.               If lX = lStrt Then
  274.                 bUnaryNeg = True
  275.                 Exit Do
  276.               ElseIf gstrParsed(lX - 1) = "(" Then 'NOT LX...
  277.                 bUnaryNeg = True
  278.                 Exit Do
  279.               End If
  280.             ElseIf gstrParsed(lX - 1) = "(" Then 'NOT STRTST...
  281.               MsgBox "Error"
  282.             End If
  283.           End If
  284.           If lTstPrec <= lStckPrec Then
  285.             'pop
  286.             plRPNpntr = plRPNpntr + 1
  287.             ReDim Preserve pstrRPN(1 To plRPNpntr) As String
  288.             pstrRPN(plRPNpntr) = strStack(lStackPntr)
  289.             lStackPntr = lStackPntr - 1
  290.             bRepeat = True
  291.           Else 'NOT LTSTPREC...
  292.             'push
  293.             lStackPntr = lStackPntr + 1
  294.             strStack(lStackPntr) = strTst
  295.             bRepeat = False
  296.           End If
  297.         Loop
  298.       End If
  299.     End If
  300.   Next lX
  301.   If lStackPntr > 0 Then
  302.     'empty stack
  303.     Do While lStackPntr > 0
  304.       'pop
  305.       plRPNpntr = plRPNpntr + 1
  306.       ReDim Preserve pstrRPN(1 To plRPNpntr) As String
  307.       pstrRPN(plRPNpntr) = strStack(lStackPntr)
  308.       lStackPntr = lStackPntr - 1
  309.     Loop
  310.   End If
  311.   Exit Sub
  312.  
  313. HaveanError:
  314.   MsgBox "Error in Parseing equation to RPN."
  315.   Resume Next
  316. End Sub
  317.  
  318. Public Sub Parser(ByVal pstrString As String)
  319.   Dim lStrPtr       As Long
  320.   Dim lY            As Long
  321.   Dim lLen          As Long
  322.   Dim lStrt         As Long
  323.   Dim bIgnoreString As Boolean
  324.   Dim bIgnoreSpace  As Boolean
  325.   Dim bInQuotes     As Boolean
  326.   Dim bSkip         As Boolean
  327.   Dim strVar        As String
  328.   Dim strWork       As String
  329.   Dim strTmp        As String
  330.   Dim strTmp2       As String
  331.   Dim strTmp3       As String
  332.   glParsedSize = 0
  333.   strWork = Trim$(pstrString)
  334.   If InStr(strWork, "[") > 0 Then
  335.     lLen = Len(strWork)
  336.     lY = lLen + 1
  337.     Do While lLen < lY
  338.       lY = lLen
  339.       strWork = Replace$(strWork, " [", "[")
  340.       strWork = Replace$(strWork, "[ ", "[")
  341.       strWork = Replace$(strWork, " ]", "]")
  342.       lLen = Len(strWork)
  343.     Loop
  344.   End If
  345.   'comments are defined by REM and '
  346.   'a string is enclosed with "
  347.   'exspected tokens =-+*/; <> >< and or xor mod
  348.   'multi character tokens must end in a blank to identify their end
  349.   'and not the beginning of a variable name
  350.   glParsedSize = -1
  351.   lStrPtr = InStr(strWork, "REM")
  352.   If lStrPtr > 0 Then
  353.     glParsedSize = glParsedSize + 1
  354.     gstrParsed(glParsedSize) = "REM"
  355.     glParsedSize = glParsedSize + 1
  356.     gstrParsed(glParsedSize) = Mid$(strWork, lStrPtr + 3)
  357.     Exit Sub
  358.   End If
  359.   If Left$(strWork, 1) = "'" Then
  360.     glParsedSize = glParsedSize + 1
  361.     gstrParsed(glParsedSize) = "'"
  362.     glParsedSize = glParsedSize + 1
  363.     gstrParsed(glParsedSize) = Mid$(strWork, lStrPtr + 3)
  364.     Exit Sub
  365.   End If
  366.   'remove comments
  367.   bIgnoreString = False
  368.   lStrPtr = 0
  369.   For lY = 1 To Len(strWork) 'find beginning of ' comments
  370.     strTmp = Mid$(strWork, lY, 1)
  371.     If strTmp = """" Then
  372.       bIgnoreString = Not bIgnoreString
  373.     ElseIf strTmp = "'" Then 'NOT STRTMP...
  374.       If Not bIgnoreString Then lStrPtr = lY
  375.       Exit For
  376.     End If
  377.   Next lY
  378.   If lStrPtr > 0 Then
  379.     'is  a ' comment
  380.     'strComments = Mid$(strWork, lStrPtr + 1)
  381.     '<:-) :WARNING: assigned only variable commented out
  382.     strWork = Trim$(Left$(strWork, lStrPtr - 1))
  383.   End If
  384.   'ucase everything but what is between ""
  385.   bInQuotes = False
  386.   For lY = 1 To Len(strWork)
  387.     strVar = Mid$(strWork, lY, 1)
  388.     If Not bInQuotes Then
  389.       If strVar <> """" Then
  390.         Mid$(strWork, lY, 1) = UCase$(strVar)
  391.       Else 'NOT STRVAR...
  392.         bInQuotes = True
  393.       End If
  394.     ElseIf strVar = """" Then 'NOT NOT...
  395.       bInQuotes = (Mid$(strWork, lY + 1, 1) = """")
  396.     End If
  397.   Next lY
  398.   'start the parseing
  399.   strVar = vbNullString
  400.   strTmp = vbNullString
  401.   bIgnoreSpace = False
  402.   bIgnoreString = False
  403.   For lStrPtr = 1 To Len(strWork)
  404.     strTmp3 = Mid$(strWork, lStrPtr, 1)
  405.     If (strTmp3 <> " ") Then
  406.       If bIgnoreSpace Then
  407.         If Len(Trim$(strVar)) > 0 Then
  408.           bIgnoreSpace = Not bIgnoreSpace
  409.           glParsedSize = glParsedSize + 1
  410.           gstrParsed(glParsedSize) = Trim$(strVar)
  411.         End If
  412.         strVar = vbNullString
  413.         bIgnoreSpace = False
  414.       End If
  415.     End If
  416.     If strTmp3 = gstrDoubleQuote Then
  417.       If Not bIgnoreString Then
  418.         If Len(Trim$(strVar)) > 0 Then
  419.           glParsedSize = glParsedSize + 1
  420.           gstrParsed(glParsedSize) = Trim$(strVar)
  421.           strVar = vbNullString
  422.         End If
  423.         strVar = strTmp3
  424.         bIgnoreString = True
  425.       ElseIf Mid$(strWork, lStrPtr + 1, 1) <> gstrDoubleQuote Then 'NOT NOT...
  426.         strVar = strVar & strTmp3
  427.         If Len(Trim$(strVar)) > 0 Then
  428.           glParsedSize = glParsedSize + 1
  429.           gstrParsed(glParsedSize) = Trim$(strVar)
  430.           strVar = vbNullString
  431.         End If
  432.         bIgnoreString = False
  433.       Else 'get the double double quote'NOT MID$(STRWORK,...
  434.         strVar = strVar & strTmp3 & Mid$(strWork, lStrPtr + 1, 1)
  435.         lStrPtr = lStrPtr + 1 '<:-) :WARNING: Modifies active For-Variable
  436.       End If
  437.     ElseIf bIgnoreString Then 'NOT STRTMP3...
  438.       strVar = strVar & strTmp3
  439.     ElseIf strTmp3 = " " Then 'BIGNORESTRING = FALSE/0
  440.       If Len(Trim$(strVar)) > 0 Then
  441.         glParsedSize = glParsedSize + 1
  442.         gstrParsed(glParsedSize) = Trim$(strVar)
  443.         strVar = vbNullString
  444.       End If
  445.       bIgnoreSpace = Not bIgnoreSpace
  446.     ElseIf strTmp3 = "," Then ' Then make a token
  447.       If Len(strVar) > 0 Then
  448.         glParsedSize = glParsedSize + 1
  449.         gstrParsed(glParsedSize) = Trim$(strVar)
  450.         strVar = vbNullString
  451.       End If
  452.     Else 'NOT STRTMP3...
  453.       bSkip = False
  454.       For lY = 2 To UBound(gstrTokens())
  455.         'multicharacter tokens will have a space before and after
  456.         'if they are in the middle of a string
  457.         lStrt = lStrPtr - 1
  458.         If lStrPtr = 1 Then
  459.           strTmp2 = gstrTokens(lY) & " "
  460.           lLen = Len(gstrTokens(lY)) + 1
  461.           lStrt = lStrPtr
  462.         ElseIf Len(strWork) - lStrPtr = Len(gstrTokens(lY)) Then 'NOT LSTRPTR...
  463.           strTmp2 = " " & gstrTokens(lY)
  464.           lLen = Len(gstrTokens(lY)) + 1
  465.         Else 'NOT LEN(STRWORK)...
  466.           strTmp2 = " " & gstrTokens(lY) & " "
  467.           lLen = Len(gstrTokens(lY)) + 2
  468.         End If
  469.         If Mid$(strWork, lStrt, lLen) = strTmp2 Then
  470.           glParsedSize = glParsedSize + 1
  471.           gstrParsed(glParsedSize) = Trim$(gstrTokens(lY))
  472.           lStrPtr = lStrPtr + Len(gstrTokens(lY)) - 1 '1 is for the "if" adder'<:-) :WARNING: Modifies active For-Variable
  473.           bSkip = True
  474.         End If
  475.       Next lY
  476.       If Not bSkip Then
  477.         If (InStr(gstrTokens(1), strTmp3) > 0) Then 'look for single char tokens
  478.           If LenB(strVar) > 0 Then
  479.             glParsedSize = glParsedSize + 1
  480.             gstrParsed(glParsedSize) = Trim$(strVar)
  481.             strVar = vbNullString
  482.           End If
  483.           glParsedSize = glParsedSize + 1
  484.           gstrParsed(glParsedSize) = Trim$(strTmp3)
  485.           strTmp2 = vbNullString
  486.           lStrPtr = lStrPtr + Len(strTmp3) - 1 'skip past multichar token in strwork'<:-) :WARNING: Modifies active For-Variable
  487.           strTmp3 = vbNullString
  488.         Else 'NOT (INSTR(GSTRTOKENS(1),...
  489.           strVar = strVar & strTmp3
  490.         End If
  491.       End If
  492.     End If
  493.   Next lStrPtr
  494.   If Len(strVar) > 0 Then
  495.     glParsedSize = glParsedSize + 1
  496.     gstrParsed(glParsedSize) = Trim$(strVar)
  497.   End If
  498.   For lY = glParsedSize + 1 To glParsedSize + 6
  499.     gstrParsed(lY) = vbNullString
  500.   Next lY
  501. End Sub
  502.  
  503. Private Sub RemoveCell(pstrArray() As String, _
  504.                             plIndex As Integer, _
  505.                             Optional plBlockLen As Long = 1)
  506.     Dim lX     As Long
  507.     Dim lY     As Long
  508.     Dim lBnd   As Long
  509.     
  510.     lBnd = (plIndex + plBlockLen - 1)
  511.     'remove array items from plIndex to plBlockLen
  512.     lY = LBound(pstrArray)
  513.     For lX = LBound(pstrArray) To UBound(pstrArray)
  514.         If lX < plIndex Or lBnd < lX Then
  515.             pstrArray(lY) = pstrArray(lX)
  516.             lY = lY + 1
  517.         End If
  518.     Next
  519.     lX = LBound(pstrArray)
  520.     lY = UBound(pstrArray) - plBlockLen
  521.     ReDim Preserve pstrArray(lX To lY)
  522. End Sub
  523.  
  524. ':)Code Fixer V3.0.9 (8/2/2005 5:15:24 AM) 5 + 638 = 643 Lines Thanks Ulli for inspiration and lots of code.
  525.  
  526.