VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
  Persistable = 0  'NotPersistable
  DataBindingBehavior = 0  'vbNone
  DataSourceBehavior  = 0  'vbNone
  MTSTransactionMode  = 0  'NotAnMTSObject
END
Attribute VB_Name = "clsFI_Interpreter"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = True
Option Explicit

Private Const m_cOP_BEGIN As String = "[\"
Private Const m_cOP_END As String = "\]"

'This constant is used to represent an expression
Private Const m_cEXPR As String = m_cOP_BEGIN & "EXPR" & m_cOP_END
'Fix Strings constants
Private Const m_cOP_QUOTE As String = m_cOP_BEGIN & "QUOTE" & m_cOP_END
Private Const m_cOP_OR As String = m_cOP_BEGIN & "O_R_"
Private Const m_cOP_AND As String = m_cOP_BEGIN & "A_N_D_"
Private Const m_cOP_GREATERTHAN As String = m_cOP_BEGIN & "GREATERTHAN" & m_cOP_END
Private Const m_cOP_LESSTHAN As String = m_cOP_BEGIN & "LESSTHAN" & m_cOP_END
Private Const m_cOP_EQUALS As String = m_cOP_BEGIN & "EQUALS" & m_cOP_END
Private Const m_cOP_EQUALSEQUALS As String = m_cOP_BEGIN & "EQUALSEQUALS" & m_cOP_END
Private Const m_cOP_PLUS As String = m_cOP_BEGIN & "PLUS" & m_cOP_END
Private Const m_cOP_MINUS As String = m_cOP_BEGIN & "MINUS" & m_cOP_END
Private Const m_cOP_MULTIPLY As String = m_cOP_BEGIN & "MULTIPLY" & m_cOP_END
Private Const m_cOP_DIVIDE As String = m_cOP_BEGIN & "DIVIDE" & m_cOP_END
Private Const m_cOP_LPAREN As String = m_cOP_BEGIN & "LPAREN" & m_cOP_END
Private Const m_cOP_RPAREN As String = m_cOP_BEGIN & "RPAREN" & m_cOP_END
Private Const m_cOP_COLON As String = m_cOP_BEGIN & "COLON" & m_cOP_END
Private Const m_cOP_TICK As String = m_cOP_BEGIN & "TICK" & m_cOP_END
Private Const m_cOP_CR As String = m_cOP_BEGIN & "CR" & m_cOP_END
Private Const m_cOP_LF As String = m_cOP_BEGIN & "LF" & m_cOP_END
Private Const m_cOP_ANDSIGN As String = m_cOP_BEGIN & "ASIGN" & m_cOP_END

Private m_cLINETERMINATORS(0 To 1) As String

'These are the various error messages that can currently occur
Private Const m_nERROR_MatchingParenNotFound As Long = 101
Private Const m_cERROR_MatchingParenNotFound As String = "ERROR(" & m_nERROR_MatchingParenNotFound & "): Closing Paren Not Found "
Private Const m_nERROR_MatchingQuoteNotFound As Long = 102
Private Const m_cERROR_MatchingQuoteNotFound As String = "ERROR(" & m_nERROR_MatchingQuoteNotFound & "): Closing Quote Not Found "
Private Const m_nERROR_InvalidParsingCharacter As Long = 103
Private Const m_cERROR_InvalidParsingCharacter As String = "ERROR(" & m_nERROR_InvalidParsingCharacter & "): Invalid Parsing Character "
Private Const m_nERROR_UnrecognizedFunction As Long = 104
Private Const m_cERROR_UnrecognizedFunction As String = "ERROR(" & m_nERROR_UnrecognizedFunction & "): Unrecognized Function "
Private Const m_nERROR_NAN As Long = 105
Private Const m_cERROR_NAN As String = "ERROR(" & m_nERROR_NAN & "): A non-number was used when a number is required "
Private Const m_nERROR_LParamIsAConstant As Long = 106
Private Const m_cERROR_LParamIsAConstant As String = "ERROR(" & m_nERROR_LParamIsAConstant & "): The LParam cannot be a constant "
Private Const m_nERROR_FunctionDefinitionDoesNotMatch As Long = 107
Private Const m_cERROR_FunctionDefinitionDoesNotMatch As String = "ERROR(" & m_nERROR_FunctionDefinitionDoesNotMatch & "): Function definitions do not match "
Private Const m_nERROR_InvalidNumberOfParamaters As Long = 108
Private Const m_cERROR_InvalidNumberOfParamaters As String = "ERROR(" & m_nERROR_InvalidNumberOfParamaters & "): The incorrect number of paramaters were passed into this function "
Private Const m_nERROR_InvalidParamType As Long = 109
Private Const m_cERROR_InvalidParamType As String = "ERROR(" & m_nERROR_InvalidParamType & "): The paramater passed into this function was of the wrong type "
Private Const m_nERROR_UnrecognizedExpr As Long = 110
Private Const m_cERROR_UnrecognizedExpr As String = "ERROR(" & m_nERROR_UnrecognizedExpr & "): Unrecognized Expression "

'Test counts
Public m_nTestCount As Long
    
Private m_oInstances As New Collection
Private m_oFunctions As New clsFI_Functions

'Where variables are stored
Private m_saVariables As New clsFOTAArray
'Where STATIC variables are stored
Private m_oStaticVariables As New clsFOTAArray

'This variable marks weather or not we are advancing or running
Private m_oAdvancingInstance As clsFI_Instance
'This variable holds the list of instances waiting to be run after we finish advancing
Private m_oRunAfterAdvance As New Collection

'These two events are a way of requesting information about a function or value from the using app
Public Event RequestingData(sExpr As String, ByRef sValue As String, sInstanceGUID As String)
Public Event RequestingFunction(sFunctionName As String, vaParamaters() As String, nNumberOfParamaters As Long, ByRef sValue As String, sInstanceGUID As String)
Public Event InstanceError(sInstanceGUID As String, sExpr As String, nErrorNumber As Long, sError As String)
Public Event InstanceFinished(sInstanceGUID As String, sResult As String)

Private Sub ErrorOccured(sExpr As String, nErrorNumber As Long, sError As String)

    If m_oAdvancingInstance Is Nothing Then
        Call Err.Raise(nErrorNumber, "Interpreter:" & sExpr, sError)
    Else
        RaiseEvent InstanceError(m_oAdvancingInstance.GUID, sExpr, nErrorNumber, sError)
    End If
    
End Sub

Public Sub RemoveFunction(sGUID As String)
    Call m_oFunctions.RemoveFunction(sGUID)
End Sub

Public Sub RemoveAllFunctions()

    While m_oFunctions.Col.Count > 0
        Call m_oFunctions.Col.Remove(1)
    Wend
    
End Sub

Public Function GetFunctionByName(sName As String) As clsFI_Function
    Set GetFunctionByName = m_oFunctions.FindFunction(sName)
End Function

Public Function GetFunctionByGUID(sGUID As String) As clsFI_Function
    Set GetFunctionByGUID = m_oFunctions.GetFunctionByGUID(sGUID)
End Function

Public Sub AddFunction(oFunction As clsFI_Function)
    Call m_oFunctions.AddFunction(oFunction)
End Sub

Public Sub StopInstance(sGUID As String)
    'On Error Resume Next
    Call m_oInstances.Remove(sGUID)
End Sub
Public Function StopInstanceIfFound(sGUID As String) As Boolean
    'On Error Resume Next
    
    Dim oLoopInstance As clsFI_Instance
    
    For Each oLoopInstance In m_oInstances
        If oLoopInstance.GUID() = sGUID Then
            Call m_oInstances.Remove(sGUID)
            Exit For
        End If
    Next oLoopInstance
        
End Function

Public Sub Advance(Optional nNumLinesProcess As Long = 1)

    Dim i As Long
    Dim s As String
    
    For i = 1 To m_oInstances.Count
        If i > m_oInstances.Count Then Exit For
        
        Set m_oAdvancingInstance = m_oInstances(i)

        If m_oAdvancingInstance.Advance(Me, nNumLinesProcess) = True Then
            'Were done!
            Call StopInstance(m_oAdvancingInstance.GUID)
            
            If m_oAdvancingInstance.RaiseEventWhenFinished = True Then
                RaiseEvent InstanceFinished(m_oAdvancingInstance.GUID, m_oAdvancingInstance.Expression)
            End If
            i = i - 1
        End If
    Next i

    Set m_oAdvancingInstance = Nothing

    'If an instance is finished, the user may start a new one in the InstanceFinished event.
    'This is bad because it will cause an infinite loop since advance time is time per instance
    '(versus total time) and the new instance... well is new.  So now we are holding back new instances
    'from being processed until after this pass.
    While m_oRunAfterAdvance.Count() > 0
        Call m_oInstances.Add(m_oRunAfterAdvance.Item(1), m_oRunAfterAdvance.Item(1).GUID())
        Call m_oRunAfterAdvance.Remove(1)
    Wend

End Sub

Public Function RunAndStore(sExpression As String) As String
    RunAndStore = SYSTEM_RunAndStore(sExpression, True)
End Function
Public Function SYSTEM_RunAndStore(sExpression As String, bFlagWhenFinished As Boolean) As String

    Dim oInstance As clsFI_Instance
    Set oInstance = New clsFI_Instance
    
    oInstance.Expression = sExpression
    SYSTEM_RunAndStore = oInstance.GUID
    oInstance.RaiseEventWhenFinished = bFlagWhenFinished

    If m_oAdvancingInstance Is Nothing Then
        Call m_oInstances.Add(oInstance, oInstance.GUID())
    Else
        Call m_oRunAfterAdvance.Add(oInstance, oInstance.GUID())
    End If

End Function

'This is the entry point into the interpretor.
Public Function Run(sExpression As String, Optional nNumLinesProcess As Long = -1, Optional ByRef bFinished As Boolean) As String
    
    Dim i As Long
    Dim sKey As String, sTemp As String
    bFinished = True
        
    Call m_saVariables.RemoveAll
    
    'The first thing we do is replace key words (like +) in strings with constants like [PLUS]
    'so that we don't have to worry about accidentlly interpreting them.
    sTemp = FixStrings(sExpression)
    'Process the command that was passed in
    Run = ProcessExpr(sTemp, m_saVariables, bFinished, nNumLinesProcess, 0, True)
    
    If Left(Trim(Run), 1) = """" And Right(Trim(Run), 1) = """" And bFinished = True Then
        Run = GetAllEncompasingQuotes(Run)
    End If
    
    'Now replace our constants back again
    Run = UnFixStrings(Run)

End Function
'This function is the work horse of the interpretor.  This is where all the recursion starts
'from and this is also where the recursion will end.
Private Function ProcessExpr(sExpr As String, ByRef oParentVariables As clsFOTAArray, ByRef bFinished As Boolean, nMax As Long, ByRef nCurDepth As Long, Optional bRemoveSetVariables As Boolean = False, Optional bLParam As Boolean = False) As String
Dim ddd As String
ddd = sExpr
    Dim sExp1 As String, sExp2 As String, sExp3 As String, sExp4 As String
    Dim sGUID As String
    Dim sTemp As String
    Dim sExpInParens As String
    Dim bIsFunction As Boolean
    Dim vaParts() As String
    Dim oVariables As clsFOTAArray
    Dim nFound As Long
    Dim nWhere As Long
    Dim nColon As Long
    Dim nCRLF As Long
    Dim i As Long, n As Long
    
    Set oVariables = oParentVariables.Copy()

    'sExpr = SearchAndReplace(sExpr, vbCrLf, "")

    'vaParts = SmartParse(sExpr, ":", nFound)
    vaParts = SmartParseArry(sExpr, m_cLINETERMINATORS, nFound)
    If nFound > 1 Then
        'Step through all the parts in the list and process them
        For i = 1 To nFound
            'Do the actual processing
            sExp1 = ProcessExpr(vaParts(i), oVariables, bFinished, nMax, nCurDepth)
            'Add the results of the processing onto the return string
            ProcessExpr = ConcatStr(ProcessExpr, ":", sExp1)
            'Did we go too far?
            If ProcessedToFar(nCurDepth, nMax) Then
                'Ok well then just slam the rest of the list (unprocessed) onto the return string
                For n = i + 1 To nFound
                    ProcessExpr = ConcatStr(ProcessExpr, ":", vaParts(n))
                Next n
                'And bail
                bFinished = False
                Exit For
            End If
        Next i
    Else
        If InStr(1, sExpr, "'") > 0 Then
            nWhere = InStr(1, sExpr, "'")
            nColon = InStr(nWhere, sExpr, ":")
            nCRLF = InStr(nWhere, sExpr, vbCrLf)
            sExp1 = Left(sExpr, InStr(1, sExpr, "'") - 1)
            If (nColon < nCRLF And nColon > 0) Or (Not nCRLF > 0 And nColon > 0) Then
                sExp2 = Mid(sExpr, nColon + 1)
            ElseIf (nCRLF < nColon And nCRLF > 0) Or (Not nColon > 0 And nCRLF > 0) Then
                sExp2 = Mid(sExpr, nCRLF + 2)
            'This means the rest of the line is comments
            Else
                sExp2 = ""
            End If

'Chris
            ProcessExpr = ProcessExpr(sExp1 & sExp2, oVariables, bFinished, nMax, nCurDepth, False) ' True)
            'ProcessExpr = ProcessExpr(sExp1 & sExp2, oParentVariables, bFinished, nMax, nCurDepth, False) ' True)
            
        'If this is a function than lets handle it
        ElseIf IsFunction(sExpr, sTemp) = True Then
            ProcessExpr = ProcessFunction(sTemp, sExpr, oVariables, bFinished, nMax, nCurDepth)
        'Parenthases suck
        ElseIf InStr(1, sExpr, "(") > 0 Then
            'Now we have to find the closing ) and get all the info in between
            sExpInParens = GetAllEncompasingParens(sExpr, sExp1)
            'Process what was inbetween to make sure it is a constant
            sExp2 = ProcessExpr(sExpInParens, oVariables, bFinished, nMax, nCurDepth, True)
            'Can we keeping going or are we out of processes?
            If ProcessedToFar(nCurDepth, nMax) Then
                'Were out.  So memorize where we are and return it.
                ProcessExpr = SearchAndReplace(sExp1, m_cEXPR, "(" & sExp2 & ")")
                bFinished = False
            Else
                'We can keep going!  So get the expression and parse into it
                sExp3 = SearchAndReplace(sExp1, m_cEXPR, sExp2)
                nCurDepth = nCurDepth + 1
                ProcessExpr = ProcessExpr(sExp3, oVariables, bFinished, nMax, nCurDepth)
            End If
        'Is their an operator we can use?
        ElseIf InStr(1, UCase(sExpr), " AND ") > 0 Then
            ProcessExpr = ProcessOperator(" AND ", sExpr, oVariables, bFinished, nMax, nCurDepth)
        ElseIf InStr(1, UCase(sExpr), " OR ") > 0 Then
            ProcessExpr = ProcessOperator(" OR ", sExpr, oVariables, bFinished, nMax, nCurDepth)
        ElseIf InStr(1, sExpr, "!=") > 0 Then
            ProcessExpr = ProcessOperator("!=", sExpr, oVariables, bFinished, nMax, nCurDepth)
        ElseIf InStr(1, sExpr, "<>") > 0 Then
            ProcessExpr = ProcessOperator("<>", sExpr, oVariables, bFinished, nMax, nCurDepth)
        ElseIf InStr(1, sExpr, "<=") > 0 Then
            ProcessExpr = ProcessOperator("<=", sExpr, oVariables, bFinished, nMax, nCurDepth)
        ElseIf InStr(1, sExpr, ">=") > 0 Then
            ProcessExpr = ProcessOperator(">=", sExpr, oVariables, bFinished, nMax, nCurDepth)
        ElseIf InStr(1, sExpr, "<") > 0 Then
            ProcessExpr = ProcessOperator("<", sExpr, oVariables, bFinished, nMax, nCurDepth)
        ElseIf InStr(1, sExpr, ">") > 0 Then
            ProcessExpr = ProcessOperator(">", sExpr, oVariables, bFinished, nMax, nCurDepth)
        ElseIf InStr(1, sExpr, "==") > 0 Then
            ProcessExpr = ProcessOperator("==", sExpr, oVariables, bFinished, nMax, nCurDepth)
        ElseIf InStr(1, sExpr, "=") > 0 Then
            ProcessExpr = ProcessOperator("=", sExpr, oVariables, bFinished, nMax, nCurDepth, False) 'True)
        ElseIf InStr(1, sExpr, "E-") > 0 Then
            ProcessExpr = ProcessOperator("E-", sExpr, oVariables, bFinished, nMax, nCurDepth)
        ElseIf InStr(1, sExpr, "E+") > 0 Then
            ProcessExpr = ProcessOperator("E+", sExpr, oVariables, bFinished, nMax, nCurDepth)
        ElseIf InStr(1, sExpr, "+") > 0 Then
            ProcessExpr = ProcessOperator("+", sExpr, oVariables, bFinished, nMax, nCurDepth)
        ElseIf InStr(1, sExpr, "&") > 0 Then
            ProcessExpr = ProcessOperator("&", sExpr, oVariables, bFinished, nMax, nCurDepth)
        ElseIf InStr(2, Trim(sExpr), "-") > 0 Then
            ProcessExpr = ProcessOperator("-", sExpr, oVariables, bFinished, nMax, nCurDepth)
        ElseIf InStr(1, sExpr, "*") > 0 Then
            ProcessExpr = ProcessOperator("*", sExpr, oVariables, bFinished, nMax, nCurDepth)
        ElseIf InStr(1, sExpr, "/") > 0 Then
            ProcessExpr = ProcessOperator("/", sExpr, oVariables, bFinished, nMax, nCurDepth)
        'Well if its not an operator, and its not a function...
        Else
            'First of all lets see if its a string
            sExpr = Trim(SearchAndReplace(sExpr, vbCrLf, ""))
            sTemp = GetAllEncompasingQuotes(sExpr)
            If bLParam = True Then
                'Now is it a valid LParam?
                If IsConstant(sExpr) = True And Len(sExpr) > 0 Then
                    Call ErrorOccured(sExpr, m_nERROR_LParamIsAConstant, m_cERROR_LParamIsAConstant & "<" & sExpr & ">")
                End If
            ElseIf IsConstant(sExpr) = False Then
                'First!!  Is it a variable?
                If GetVariable(oVariables, sExpr, sExp2) = True Then
                    sTemp = sExp2
                    nCurDepth = nCurDepth + 1
                'Is it a static variable?
                ElseIf GetVariable(m_oStaticVariables, sExpr, sExp2) = True Then
                    sTemp = sExp2
                    nCurDepth = nCurDepth + 1
                '04/08/2003 Chris Hill  You don't have to initialize statics, just define them.
                ElseIf Left(UCase(Trim(sExpr)), 7) = "STATIC " Then
                    sExp1 = Mid(Trim(sExpr), 7)
                    If GetVariable(m_oStaticVariables, sExp1, sExp2) = False Then
                        Call SetVariable(m_oStaticVariables, sExp1, sExp2)
                    Else
                        'Do nothing if we find it?  We don't want to return it of course.
                    End If
                    sTemp = ""
                Else
                    'Pass back the GUID
                    If m_oAdvancingInstance Is Nothing Then
                        sGUID = ""
                    Else
                        sGUID = m_oAdvancingInstance.GUID
                    End If
                    'Ask the user
                    RaiseEvent RequestingData(sExpr, sTemp, sGUID)
                    'Did they change the value?  If not then we didn't do any work
                    If Not sTemp = sExpr Then
                        nCurDepth = nCurDepth + 1
                        
                        'This means two things, we have to wrap it in " if it wasn't already,
                        'and we have to fix its strings
                        'If Not Left(Trim(sTemp), 1) = """" Then
                        '    sTemp = """" & sTemp
                        'End If
                        'If Not Right(Trim(sTemp), 1) = """" Then
                        '    sTemp = sTemp & """"
                        'End If
                        sTemp = FixStrings(sTemp)
                    Else
                        Call ErrorOccured(sExpr, m_nERROR_UnrecognizedExpr, m_cERROR_UnrecognizedExpr & "<" & sExpr & ">")
                    End If
                End If
            'Don't do anything
            Else
                sTemp = sExpr
            End If
            'Return the answer we got
            ProcessExpr = sTemp
        End If
    End If
    
    'This is silly I know.  But I don't know where else it belongs.
    'ProcessExpr = SearchAndReplace(ProcessExpr, vbCrLf, "")
    If SearchAndReplace(ProcessExpr, vbCrLf, "") = "" Then
        ProcessExpr = ""
    End If
    
    ProcessExpr = GetVariableDiff(ProcessExpr, oParentVariables, oVariables, _
                                  (bFinished = False And ProcessedToFar(nCurDepth, nMax) Or bRemoveSetVariables = False), _
                                  ProcessedToFar(nCurDepth, nMax))
    
End Function
'This function has a complicated task.  It must parse out all the function information and
'decide how to handle the paramaters passed in.  Oh but speaking of paramaters it also must
'make sure that the paramaters are processed as far as they will go.
Private Function ProcessFunction(sFunction As String, sExpr As String, ByRef oParentVariables As clsFOTAArray, ByRef bFinished As Boolean, nMax As Long, ByRef nCurDepth As Long) As String
    
    Dim sExp1 As String
    Dim sGUID As String
    Dim sLeft As String, sRight As String, sExpInParens As String
    Dim sTemp1 As String, sTemp2 As String
    Dim vaTemp() As String, vaFunctionParams() As String
    Dim nFound As Long
    Dim sInnards As String
    Dim oFunction As clsFI_Function
    Dim sAdd As String, sRemove As String
    Dim i As Long, n As Long
    Dim sConditionPart As String, sTemp As String
    Dim bItsTime As Boolean
    Dim nTemp As Long
    
    'Get everything left and right of our function
    sLeft = Left(sExpr, InStr(1, UCase(sExpr), UCase(sFunction)) - 1)
    sTemp1 = Mid(Trim(sExpr), InStr(1, UCase(Trim(sExpr)), UCase(sFunction) & "("))
    sExpInParens = GetAllEncompasingParens(sTemp1, sTemp1)
    sRight = Mid(sTemp1, InStr(1, sTemp1, m_cEXPR) + Len(m_cEXPR))

    'Parse out our innards
    vaTemp = SmartParse(sExpInParens, ",", nFound)
    If Len(Trim(sExpInParens)) = 0 Then nFound = 0
    'Now step through all the innards and make sure we have processed them as far as they will go
    For i = 1 To nFound
            
        If ProcessedToFar(nCurDepth, nMax) = True Then
            'Do nothing
        ElseIf UCase(sFunction) = "WHILE" Then
            If nFound = 2 Then
                vaTemp = SmartParse(sExpInParens & "," & vaTemp(2), ",", nFound)
            End If
            If i = 1 Then
                'This stage is free.  If it wasn't their is a chance we could loop forever
                nTemp = nCurDepth
                sConditionPart = ProcessExpr(vaTemp(i), oParentVariables, bFinished, -1, nCurDepth)
                sConditionPart = GetAllEncompasingQuotes(sConditionPart)
                nCurDepth = nTemp
            'Are we done?  If the for part is false we are
            ElseIf Trim(sConditionPart) = False Then
                i = nFound + 1
            ElseIf i = 2 Then
                'Process whats in the for's loop area
                vaTemp(i + 1) = ProcessExpr(vaTemp(i + 1), oParentVariables, bFinished, nMax, nCurDepth)
                'vaTemp(i + 1) = GetAllEncompasingQuotes(vaTemp(i + 1))
                'This means we finished the loop area!
                If bFinished = True And Not (ProcessedToFar(nCurDepth, nMax)) Then
                    'Reset the loop area
                    vaTemp(3) = vaTemp(2)
                    'Start us over at the beginning of the loop area.  (1+1 = 2 which is where we REALLY want to start)
                    i = 0
                Else
                    'Otherwise loop us again.  (1+1 = 2 which is where we really want to start)
                    i = 1
                End If
            End If
        ElseIf UCase(sFunction) = "FOR" Then
            'First of all if this is our first time through add in the code we will walk through
            If nFound = 4 Then
                vaTemp = SmartParse(sExpInParens & "," & vaTemp(4), ",", nFound)
            End If
            'Process the up front criteria
            If i = 1 Then
                vaTemp(i) = ProcessExpr(vaTemp(i), oParentVariables, bFinished, nMax, nCurDepth)
                vaTemp(i) = GetAllEncompasingQuotes(vaTemp(i))
            'Process the "Should we loop" criteria
            ElseIf i = 2 Then
                'This stage is free.  If it wasn't their is a chance we could loop forever
                nTemp = nCurDepth
                sConditionPart = ProcessExpr(vaTemp(i), oParentVariables, bFinished, -1, nCurDepth)
                sConditionPart = GetAllEncompasingQuotes(sConditionPart)
                nCurDepth = nTemp
            'Are we done?  If the for part is false we are
            ElseIf sConditionPart = False Then
                i = nFound + 1
            ElseIf i = 4 Then
                'Process whats in the for's loop area
                vaTemp(i + 1) = ProcessExpr(vaTemp(i + 1), oParentVariables, bFinished, nMax, nCurDepth)
                vaTemp(i + 1) = GetAllEncompasingQuotes(vaTemp(i + 1))
                'This means we finished the loop area!
                If bFinished = True And Not (ProcessedToFar(nCurDepth, nMax)) Then
                    'Reset the loop area
                    vaTemp(5) = vaTemp(4)
                    'Process the "We are done" incramenter.  This stage is free.
                    nTemp = nCurDepth
                    sTemp = ProcessExpr(vaTemp(3), oParentVariables, bFinished, -1, nCurDepth)
                    sTemp = GetAllEncompasingQuotes(sTemp)
                    nCurDepth = nTemp
                    'Start us over at the beginning of the loop area.  (1+1 = 2 which is where we REALLY want to start)
                    i = 1
                Else
                    'Otherwise loop us again.  (3+1 = 4 which is where we really want to start)
                    i = 3
                End If
            End If
        'If its an if statement we don't want to blindly process all parts.
        ElseIf UCase(sFunction) = "IF" And i = 2 Then
            If IsBoolean(vaTemp(1)) = True Then
                If vaTemp(1) = False Then i = 3
                vaTemp(i) = ProcessExpr(vaTemp(i), oParentVariables, bFinished, nMax, nCurDepth)
                vaTemp(i) = GetAllEncompasingQuotes(vaTemp(i))
                i = nFound + 1
            Else
                Call ErrorOccured(vaTemp(1), m_nERROR_InvalidParamType, m_cERROR_InvalidParamType)
            End If
        Else
            'Process the innard to make sure were done.
            vaTemp(i) = ProcessExpr(vaTemp(i), oParentVariables, bFinished, nMax, nCurDepth)
        End If
        'Are we done?
        If ProcessedToFar(nCurDepth, nMax) Then
            'Yep were done. Assemble our return expression
            sInnards = ""
            For n = 1 To nFound
                sInnards = sInnards & vaTemp(n)
                If n < nFound Then sInnards = sInnards & ","
            Next n
            'Set our return string and get outta dodge
            ProcessFunction = sLeft & sFunction & "(" & sInnards & ")" & sRight
            bFinished = False
            Exit Function
        End If
    Next i
    
    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    'System Functions
    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    If UCase(sFunction) = "RETURN" Then
        sExpInParens = vaTemp(1)
    ElseIf UCase(sFunction) = "SETVARIABLE" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        Call SetVariable(oParentVariables, vaTemp(1), vaTemp(2))
        sExpInParens = ""
        nCurDepth = nCurDepth - 1
    ElseIf UCase(sFunction) = "ISDEFINED" Then
        sExpInParens = (GetVariable(oParentVariables, vaTemp(1), sTemp1))
    ElseIf UCase(sFunction) = "REMOVEVARIABLE" Then
        Call oParentVariables.Remove(vaTemp(1))
        sExpInParens = ""
        nCurDepth = nCurDepth - 1
    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    'VB String Functions
    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    ElseIf UCase(sFunction) = "ASC" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        sExpInParens = Asc(vaTemp(1))
    ElseIf UCase(sFunction) = "CHR" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        sExpInParens = """" & Chr(vaTemp(1)) & """"
        sExpInParens = FixStrings(sExpInParens)
    ElseIf UCase(sFunction) = "FORMAT" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        vaTemp(2) = GetAllEncompasingQuotes(vaTemp(2))
        vaTemp(3) = GetAllEncompasingQuotes(vaTemp(3))
        If nFound = 3 Then
            Call Format(vaTemp(1), vaTemp(2), vaTemp(3))
        ElseIf nFound = 4 Then
            Call Format(vaTemp(1), vaTemp(2), vaTemp(3), vaTemp(4))
        Else
            Call Format(vaTemp(1), vaTemp(2))
        End If
    ElseIf UCase(sFunction) = "INSTR" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        vaTemp(2) = GetAllEncompasingQuotes(vaTemp(2))
        vaTemp(3) = GetAllEncompasingQuotes(vaTemp(3))
        sExpInParens = InStr(vaTemp(1), vaTemp(2), vaTemp(3))
    ElseIf UCase(sFunction) = "LCASE" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        sExpInParens = """" & LCase(vaTemp(1)) & """"
    ElseIf UCase(sFunction) = "LEFT" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        vaTemp(2) = GetAllEncompasingQuotes(vaTemp(2))
        sExpInParens = """" & Left(vaTemp(1), vaTemp(2)) & """"
    ElseIf UCase(sFunction) = "LEN" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        sExpInParens = Len(vaTemp(1))
    ElseIf UCase(sFunction) = "LTRIM" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        sExpInParens = """" & LTrim(vaTemp(1)) & """"
    ElseIf UCase(sFunction) = "MID" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        If nFound = 2 Then
            sExpInParens = """" & Mid(vaTemp(1), vaTemp(2)) & """"
        ElseIf nFound = 3 Then
            sExpInParens = """" & Mid(vaTemp(1), vaTemp(2), vaTemp(3)) & """"
        Else
            Call ErrorOccured(sExpr, m_nERROR_InvalidNumberOfParamaters, m_cERROR_InvalidNumberOfParamaters)
        End If
    ElseIf UCase(sFunction) = "RIGHT" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        sExpInParens = """" & Right(vaTemp(1), vaTemp(2)) & """"
    ElseIf UCase(sFunction) = "RTRIM" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        sExpInParens = """" & RTrim(vaTemp(1)) & """"
    ElseIf UCase(sFunction) = "SPACE" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        sExpInParens = """" & Space(vaTemp(1)) & """"
    ElseIf UCase(sFunction) = "STRCOMP" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        sExpInParens = StrComp(vaTemp(1), vaTemp(2))
    ElseIf UCase(sFunction) = "STRING" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        sExpInParens = """" & String(vaTemp(1), vaTemp(2)) & """"
    ElseIf UCase(sFunction) = "TRIM" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        sExpInParens = """" & Trim(vaTemp(1)) & """"
    ElseIf UCase(sFunction) = "UCASE" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        sExpInParens = """" & UCase(vaTemp(1)) & """"
    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    'VB Date & Time Functions
    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    ElseIf UCase(sFunction) = "DATE" Then
        sExpInParens = """" & Date & """"
    ElseIf UCase(sFunction) = "DATEADD" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        vaTemp(2) = GetAllEncompasingQuotes(vaTemp(2))
        vaTemp(3) = GetAllEncompasingQuotes(vaTemp(3))
        sExpInParens = """" & DateAdd(vaTemp(1), vaTemp(2), vaTemp(3)) & """"
    ElseIf UCase(sFunction) = "DATEDIFF" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        vaTemp(2) = GetAllEncompasingQuotes(vaTemp(2))
        vaTemp(3) = GetAllEncompasingQuotes(vaTemp(3))
        sExpInParens = """" & DateDiff(vaTemp(1), vaTemp(2), vaTemp(3)) & """"
    ElseIf UCase(sFunction) = "DATEPART" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        vaTemp(2) = GetAllEncompasingQuotes(vaTemp(2))
        sExpInParens = """" & DatePart(vaTemp(1), vaTemp(2)) & """"
    ElseIf UCase(sFunction) = "DATESERIAL" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        vaTemp(2) = GetAllEncompasingQuotes(vaTemp(2))
        vaTemp(3) = GetAllEncompasingQuotes(vaTemp(3))
        sExpInParens = """" & DateSerial(vaTemp(1), vaTemp(2), vaTemp(3)) & """"
    ElseIf UCase(sFunction) = "DATEVALUE" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        sExpInParens = """" & DateValue(vaTemp(1)) & """"
    ElseIf UCase(sFunction) = "DAY" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        sExpInParens = """" & Day(vaTemp(1)) & """"
    ElseIf UCase(sFunction) = "HOUR" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        sExpInParens = """" & Hour(vaTemp(1)) & """"
    ElseIf UCase(sFunction) = "ISDATE" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        sExpInParens = IsDate(vaTemp(1))
    ElseIf UCase(sFunction) = "MINUTE" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        sExpInParens = """" & Minute(vaTemp(1)) & """"
    ElseIf UCase(sFunction) = "MONTH" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        sExpInParens = """" & Month(vaTemp(1)) & """"
    ElseIf UCase(sFunction) = "NOW" Then
        sExpInParens = """" & Now() & """"
    ElseIf UCase(sFunction) = "SECOND" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        sExpInParens = """" & Second(vaTemp(1)) & """"
    ElseIf UCase(sFunction) = "TIME" Then
        sExpInParens = """" & Time() & """"
    ElseIf UCase(sFunction) = "TIMER" Then
        sExpInParens = Timer()
    ElseIf UCase(sFunction) = "TIMESERIAL" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        vaTemp(2) = GetAllEncompasingQuotes(vaTemp(2))
        vaTemp(3) = GetAllEncompasingQuotes(vaTemp(3))
        sExpInParens = """" & TimeSerial(vaTemp(1), vaTemp(2), vaTemp(3)) & """"
    ElseIf UCase(sFunction) = "TIMEVALUE" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        sExpInParens = """" & TimeValue(vaTemp(1)) & """"
    ElseIf UCase(sFunction) = "WEEKDAY" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        sExpInParens = """" & Weekday(vaTemp(1)) & """"
    ElseIf UCase(sFunction) = "YEAR" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        sExpInParens = """" & Year(vaTemp(1)) & """"
    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    'VB Conversion Functions
    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    ElseIf UCase(sFunction) = "CINT" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        sExpInParens = GetAllEncompasingQuotes(vaTemp(1))
    ElseIf UCase(sFunction) = "CSTR" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        sExpInParens = """" & vaTemp(1) & """"
    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    'Other VB Functions
    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    ElseIf UCase(sFunction) = "WAIT" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        sExpInParens = "WAITTILL(" & Timer + vaTemp(1) & ")"
    ElseIf UCase(sFunction) = "WAITTILL" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        If nMax = -1 Then
            bItsTime = False
            While bItsTime = False
                'Does it cross the midnight line?
                If vaTemp(1) < 1000 And Timer > 70000 Then
                    'Not time yet
                    bItsTime = False
                ElseIf vaTemp(1) > 70000 And Timer < 1000 Then
                    'Time has passed
                    bItsTime = True
                Else
                    bItsTime = (Timer >= vaTemp(1))
                End If
                DoEvents
            Wend
        Else
            'Does it cross the midnight line?
            If vaTemp(1) < 1000 And Timer > 70000 Then
                'Not time yet
                bItsTime = False
            ElseIf vaTemp(1) > 70000 And Timer < 1000 Then
                'Time has passed
                bItsTime = True
            Else
                bItsTime = (Timer >= vaTemp(1))
            End If
            If bItsTime = True Then
                sExpInParens = ""
            Else
                sExpInParens = "WAITTILL(" & vaTemp(1) & ")"
            End If
        End If
    ElseIf UCase(sFunction) = "WAITCYCLES" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        If nMax = -1 Or _
           (nMax > -1 And nMax - nCurDepth > 0 And nMax - nCurDepth > vaTemp(1)) Then
                nCurDepth = nCurDepth + vaTemp(1) - 1
                sExpInParens = ""
        Else
                vaTemp(1) = vaTemp(1) - (nMax - nCurDepth)
                nCurDepth = nMax - 1
                sExpInParens = "WAITCYCLES(" & vaTemp(1) & ")"
        End If
    ElseIf UCase(sFunction) = "RND" Then
        sExpInParens = Rnd()
        sExpInParens = FormatNumber(sExpInParens, 7)
    ElseIf UCase(sFunction) = "RANDOM" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        vaTemp(2) = GetAllEncompasingQuotes(vaTemp(2))
        sExpInParens = Int((vaTemp(2) - vaTemp(1) + 1) * Rnd + vaTemp(1))
    ElseIf UCase(sFunction) = "FOR" Then
        sExpInParens = ""
    ElseIf UCase(sFunction) = "WHILE" Then
        sExpInParens = ""
    ElseIf UCase(sFunction) = "SPLIT" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        vaTemp(2) = GetAllEncompasingQuotes(vaTemp(2))
        vaTemp(3) = GetAllEncompasingQuotes(vaTemp(3))
        vaFunctionParams = Split(vaTemp(1), vaTemp(3))
        sExpInParens = """" & vaFunctionParams(vaTemp(2)) & """"
    ElseIf UCase(sFunction) = "IIF" Or UCase(sFunction) = "IF" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        vaTemp(2) = GetAllEncompasingQuotes(vaTemp(2))
        vaTemp(3) = GetAllEncompasingQuotes(vaTemp(3))
        
        If IsBoolean(vaTemp(1)) = True Then
            If vaTemp(1) = True Then
                sExpInParens = vaTemp(2)
            Else
                sExpInParens = vaTemp(3)
            End If
            If Len(sExpInParens) > 0 And IsNumeric(sExpInParens) = False And IsBoolean(sExpInParens) = False Then
                sExpInParens = """" & sExpInParens & """"
            End If
        Else
            Call ErrorOccured(vaTemp(1), m_nERROR_InvalidParamType, m_cERROR_InvalidParamType)
        End If
    ElseIf UCase(sFunction) = "FLOOR" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        Call IsNAN(sExpr, vaTemp(1))
        If vaTemp(1) < 0 Then
            sExpInParens = Fix(vaTemp(1)) - 1
        Else
            sExpInParens = Fix(vaTemp(1))
        End If
    ElseIf UCase(sFunction) = "CEALING" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        Call IsNAN(sExpr, vaTemp(1))
        If vaTemp(1) < 0 Then
            sExpInParens = Fix(vaTemp(1))
        Else
            sExpInParens = Fix(vaTemp(1)) + 1
        End If
    ElseIf UCase(sFunction) = "MIN" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        vaTemp(2) = GetAllEncompasingQuotes(vaTemp(2))
        Call IsNAN(sExpr, vaTemp(1))
        Call IsNAN(sExpr, vaTemp(2))
        sExpInParens = Min(CLng(vaTemp(1)), CLng(vaTemp(2)))
    ElseIf UCase(sFunction) = "MAX" Then
        vaTemp(1) = GetAllEncompasingQuotes(vaTemp(1))
        vaTemp(2) = GetAllEncompasingQuotes(vaTemp(2))
        Call IsNAN(sExpr, vaTemp(1))
        Call IsNAN(sExpr, vaTemp(2))
        sExpInParens = Max(CLng(vaTemp(1)), CLng(vaTemp(2)))
    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    'Dynamic & User Functions
    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    Else
        'Maybe its a defined function?
        Set oFunction = GetFunctionByName(sFunction)
        If Not oFunction Is Nothing Then
            'So lets do the whole calling thing.
            sTemp1 = GetAllEncompasingParens(oFunction.GetLine(1))
            vaFunctionParams = Split(Trim(sTemp1), ",")
            If (UBound(vaFunctionParams) = -1 And nFound = 0) Or _
               (UBound(vaFunctionParams) + 1 = nFound) Then
                    sAdd = ""
                    sRemove = ""
                    If UBound(vaFunctionParams) > -1 Then
                        For i = 0 To UBound(vaFunctionParams)
                            If Len(sAdd) > 0 Then sAdd = sAdd & ":"
                            If Len(sRemove) > 0 Then sRemove = sRemove & ":"
                            sAdd = sAdd & "SetVariable(""" & vaFunctionParams(i) & """," & vaTemp(i + 1) & ")"
                        Next i
                    End If
                    sExpInParens = ""
                    For i = 2 To oFunction.Count
                        If Len(sExpInParens) > 0 Then sExpInParens = sExpInParens & ":"
                        If Len(oFunction.GetLine(i)) > 0 Then sExpInParens = sExpInParens & oFunction.GetLine(i)
                    Next i
                    
                    sExpInParens = "(" & sAdd & ":" & sExpInParens & ")" '":" & sRemove & ")"
                    sExpInParens = FixStrings(sExpInParens)
            Else
                Call ErrorOccured(sExpr, m_nERROR_FunctionDefinitionDoesNotMatch, m_cERROR_FunctionDefinitionDoesNotMatch & ".  <" & sTemp1 & "," & sExpInParens & ">")
            End If
        Else
            'Maybe its a user defined function
            sTemp1 = ""
            For i = 1 To nFound
                vaTemp(i) = UnFixStrings(vaTemp(i))
                If Len(sTemp1) > 0 Then
                    sTemp1 = sTemp1 & "," & vaTemp(i)
                Else
                    sTemp1 = vaTemp(i)
                End If
                vaTemp(i) = GetAllEncompasingQuotes(vaTemp(i))
            Next i
            sExp1 = sFunction & "(" & Mid(sTemp1, 1) & ")"
            'Get the guid of the instance if their is one
            If m_oAdvancingInstance Is Nothing Then
                sGUID = ""
            Else
                sGUID = m_oAdvancingInstance.GUID()
            End If
            RaiseEvent RequestingFunction(sFunction, vaTemp, nFound, sExp1, sGUID)
            'Did they give us an answer?
            If sExp1 = sFunction & "(" & Mid(sTemp1, 1) & ")" Then
                'Nope.  So raise an error and undo the work
                Call ErrorOccured(sExpr, m_nERROR_UnrecognizedFunction, m_cERROR_UnrecognizedFunction & " <" & sFunction & ">")
                nCurDepth = nCurDepth - 1
            Else
                'Yep they did.
                sExpInParens = FixStrings(sExp1)
            End If
        End If
    End If
        
    nCurDepth = nCurDepth + 1
    'Parse the entire thing
    ProcessFunction = ProcessExpr(sLeft & sExpInParens & sRight, oParentVariables, bFinished, nMax, nCurDepth)
        
    'This is silly I know.  But I don't know where else it belongs.
    If SearchAndReplace(ProcessFunction, vbCrLf, "") = "" Then
        ProcessFunction = ""
    End If
    
End Function

'It is the job of this function to handle operators.  Its setup to be generic, but the minus operator messes things up.
'Thats because -1 is a valid number.
Private Function ProcessOperator(sOperator As String, sExpr As String, ByRef oParentVariables As clsFOTAArray, ByRef bFinished As Boolean, nMax As Long, ByRef nCurDepth As Long, Optional bLParam As Boolean = False) As String

    Dim sExp1 As String, sExp2 As String, sExp3 As String, sExp4 As String
    Dim nStart As Long
    Dim sTemp As String
    Dim i As Long
   
    'The minus sign has to be very different.  Gosh darn it.
    If sOperator = "-" Then
        'If this is the minus sign we want to add the negative of sExp2 to sExp1.  to do this
        'we push our negative sign onto the second expression.
        nStart = 2
        sExp2 = Mid(sExpr, InStr(nStart, UCase(sExpr), sOperator))
    Else
        'This is how most things work.  We just want a plain jane left and right.
        nStart = 1
        sExp2 = Mid(sExpr, InStr(nStart, UCase(sExpr), sOperator) + Len(sOperator))
    End If
    
    'Get the left hand component and parse it to make sure its at its most basic
    sExp1 = Left(sExpr, InStr(nStart, UCase(sExpr), sOperator) - 1)

    'If its the = sign we don't want to evalutate it
    If Not sOperator = "=" Then
        sExp1 = ProcessExpr(sExp1, oParentVariables, bFinished, nMax, nCurDepth, , bLParam)
    End If
    
    'Did we use up all our processing time?
    If ProcessedToFar(nCurDepth, nMax) Then
        'If we did and were a minus sign, the minus is in sExp2
        If sOperator = "-" Then
            ProcessOperator = sExp1 & sExp2
        'But were not using "-" so we have to add the operator back in
        Else
            ProcessOperator = sExp1 & " " & sOperator & " " & sExp2
        End If
        bFinished = False
    Else
        'The E- and E+ signs are very different
        If sOperator = "E+" Or sOperator = "E-" Then
            'For them, sExp2 will be the right hand part of the equantion.  For example...
            '1E-3 - 4... sExp2 will hold the -4, sExp3 will hold the -3.
            sExp3 = Trim(sExp2)
            sExp2 = ""
            For i = 1 To Len(sExp3)
                If IsNumeric(Mid(sExp3, i, 1)) = False Then
                    sExp2 = Mid(sExp3, i)
                    sExp3 = Left(sExp3, i - 1)
                    Exit For
                End If
            Next i
                
        Else
            sExp2 = ProcessExpr(sExp2, oParentVariables, bFinished, nMax, nCurDepth)
        End If
        
        'Did we use up all our processing?  Lets hope not.
        If ProcessedToFar(nCurDepth, nMax) Then
            If sOperator = "-" Then
                ProcessOperator = sExp1 & sExp2
            Else
                ProcessOperator = sExp1 & " " & sOperator & sExp2
            End If
            bFinished = False
        'Excellant!  Lets use our operator.
        Else
            If sOperator = "*" Then
                Call IsNAN(sExpr, sExp1)
                Call IsNAN(sExpr, sExp2)
                ProcessOperator = (sExp1 * sExp2)
            ElseIf sOperator = " AND " Then
                If IsBoolean(sExp1) = True And IsBoolean(sExp2) = True Then
                    ProcessOperator = (CBool(Trim(sExp1)) And CBool(Trim(sExp2)))
                Else
                    ProcessOperator = False
                End If
            ElseIf sOperator = " OR " Then
                If IsBoolean(sExp1) And IsBoolean(sExp2) = True Then
                    ProcessOperator = (CBool(Trim(sExp1)) Or CBool(Trim(sExp2)))
                ElseIf IsBoolean(sExp1) = True Then
                    ProcessOperator = CBool(Trim(sExp1))
                ElseIf IsBoolean(sExp2) = True Then
                    ProcessOperator = CBool(Trim(sExp2))
                Else
                    ProcessOperator = False
                End If
            ElseIf sOperator = "<>" Or sOperator = "!=" Then
                'Normally I would worry about stripping off our quotes... but we have to
                'both be strings and the return value is a boolean so we should be ok.
                sExp1 = GetAllEncompasingQuotes(sExp1)
                sExp2 = GetAllEncompasingQuotes(sExp2)
                'Should we do a numerical comparasin?
                If IsNumeric(sExp1) = True And IsNumeric(sExp2) = True Then
                    ProcessOperator = (CDbl(sExp1) <> CDbl(sExp2))
                'Nope were strings.
                Else
                    ProcessOperator = (sExp1 <> sExp2)
                End If
            ElseIf sOperator = "<=" Then
                'Get rid of any quotes.
                sExp1 = GetAllEncompasingQuotes(sExp1)
                sExp2 = GetAllEncompasingQuotes(sExp2)

                If IsNumeric(sExp1) = True And IsNumeric(sExp2) = True Then
                    ProcessOperator = (CDbl(sExp1) <= CDbl(sExp2))
                Else
                    ProcessOperator = (sExp1 <= sExp2)
                End If
                
            ElseIf sOperator = ">=" Then
                'Get rid of any quotes.
                sExp1 = GetAllEncompasingQuotes(sExp1)
                sExp2 = GetAllEncompasingQuotes(sExp2)

                If IsNumeric(sExp1) = True And IsNumeric(sExp2) = True Then
                    ProcessOperator = (CDbl(sExp1) > CDbl(sExp2))
                Else
                    ProcessOperator = (sExp1 >= sExp2)
                End If
            ElseIf sOperator = ">" Then
                'Get rid of any quotes.
                sExp1 = GetAllEncompasingQuotes(sExp1)
                sExp2 = GetAllEncompasingQuotes(sExp2)

                If IsNumeric(sExp1) = True And IsNumeric(sExp2) = True Then
                    ProcessOperator = (CDbl(sExp1) > CDbl(sExp2))
                Else
                    ProcessOperator = (sExp1 > sExp2)
                End If
            ElseIf sOperator = "<" Then
                'Get rid of any quotes.
                sExp1 = GetAllEncompasingQuotes(sExp1)
                sExp2 = GetAllEncompasingQuotes(sExp2)

                If IsNumeric(sExp1) = True And IsNumeric(sExp2) = True Then
                    ProcessOperator = (CDbl(sExp1) < CDbl(sExp2))
                Else
                    ProcessOperator = (sExp1 < sExp2)
                End If
            ElseIf sOperator = "==" Then
                'Get rid of any quotes.
                sExp3 = sExp1
                sExp1 = GetAllEncompasingQuotes(sExp1)
                sExp2 = GetAllEncompasingQuotes(sExp2)

                If IsBoolean(sExp1) = True And IsBoolean(sExp2) = True Then
                    ProcessOperator = (CBool(Trim(sExp1)) = CBool(Trim(sExp2)))
                ElseIf IsNumeric(sExp1) = True And IsNumeric(sExp2) = True Then
                    ProcessOperator = (CDbl(sExp1) = CDbl(sExp2))
                Else
                    ProcessOperator = (sExp1 = sExp2)
                End If
            ElseIf sOperator = "=" Then
                'Get rid of any quotes.
                sExp3 = sExp1
                'sExp1 = GetAllEncompasingQuotes(sExp1)
                'sExp2 = GetAllEncompasingQuotes(sExp2)

                'Introducing Static!!
                If Left(UCase(Trim(sExp1)), 7) = "STATIC " Then
                    sExp1 = Mid(Trim(sExp1), 7)
                    Call SetVariable(m_oStaticVariables, sExp1, sExp2)
                'Or is it already defined as a static?
                ElseIf GetVariable(m_oStaticVariables, sExp1, sTemp) = True Then
                    Call SetVariable(m_oStaticVariables, sExp1, sExp2)
                Else
                    'This means the left hand side wasn't a string!!
                    Call SetVariable(oParentVariables, sExp1, sExp2)
                End If
                
                ProcessOperator = ""
                
            ElseIf sOperator = "E-" Then
                If InStr(1, sExp1, ".") > 0 Then
                    ProcessOperator = FormatNumber(Trim(sExp1) & "E-" & sExp3, Len(sExp1) - InStr(1, sExp1, ".") + CLng(sExp3))
                Else
                    ProcessOperator = FormatNumber(Trim(sExp1) & "E-" & sExp3, CLng(sExp3))
                End If
                ProcessOperator = ProcessOperator & sExp2
                ProcessOperator = ProcessExpr(ProcessOperator, oParentVariables, bFinished, nMax, nCurDepth, , bLParam)
            ElseIf sOperator = "E+" Then
                If InStr(1, sExp1, ".") > 0 Then
                    ProcessOperator = FormatNumber(Trim(sExp1) & "E+" & sExp3, Max(0, Len(sExp1) - InStr(1, sExp1, ".") - CLng(sExp3)))
                Else
                    ProcessOperator = FormatNumber(Trim(sExp1) & "E+" & sExp3, 0)
                End If
                ProcessOperator = ProcessOperator & sExp2
                ProcessOperator = ProcessExpr(ProcessOperator, oParentVariables, bFinished, nMax, nCurDepth, , bLParam)
            ElseIf sOperator = "+" Or sOperator = "&" Then
                If IsNumeric(sExp1) = True And IsNumeric(sExp2) = True And sOperator = "+" Then
                    ProcessOperator = CDbl(sExp1) + CDbl(sExp2)
                ElseIf Len(sExp1) = 0 And IsNumeric(sExp2) = True And sOperator = "+" Then
                    ProcessOperator = CDbl(sExp2)
                Else
                    'Convert the strings to... well strings.  If the string is a number
                    'that was not in "" then trim it.
                    sExp3 = sExp1
                    sExp1 = GetAllEncompasingQuotes(sExp1)
                    If sExp1 = sExp3 And IsNumeric(sExp1) = True Then
                        sExp1 = Trim(sExp1)
                    End If
                    sExp4 = sExp2
                    sExp2 = GetAllEncompasingQuotes(sExp2)
                    If sExp2 = sExp4 And IsNumeric(sExp2) = True Then
                        sExp2 = Trim(sExp2)
                    End If
                    ProcessOperator = ("""" & sExp1 & sExp2 & """")
                End If
            ElseIf sOperator = "/" Then
                Call IsNAN(sExpr, sExp1)
                Call IsNAN(sExpr, sExp2)
                ProcessOperator = (sExp1 / sExp2)
            ElseIf sOperator = "-" Then
                If IsNumeric(sExp1) = True And IsNumeric(sExp2) = True Then
                    ProcessOperator = CDbl(sExp1) + CDbl(sExp2)
                Else
                    ProcessOperator = (sExp1 + sExp2)
                End If
            End If
            nCurDepth = nCurDepth + 1
        End If
    End If

    'This is silly I know.  But I don't know where else it belongs.
    If SearchAndReplace(ProcessOperator, vbCrLf, "") = "" Then
        ProcessOperator = ""
    End If

End Function

Private Sub IsNAN(sExpr As String, sNAN As String)
    If IsNumeric(sNAN) = False Then
        Call ErrorOccured(sExpr, m_nERROR_NAN, m_cERROR_NAN & "(" & sNAN & ")")
    End If
End Sub

Private Function IsConstant(sExpr As String) As Boolean
    Dim sTemp As String
    sTemp = GetAllEncompasingQuotes(sExpr)
    IsConstant = (Not sTemp = sExpr Or IsNumeric(sTemp) = True Or IsBoolean(sTemp) = True Or Len(sTemp) = 0)
End Function
Private Function ProcessedToFar(nCurDepth As Long, nMax As Long) As Boolean
    ProcessedToFar = (nCurDepth >= nMax And nMax > -1)
End Function
Private Function ConcatStr(sOrig As String, sSeperator As String, sNewPart As String) As String

    If Len(Trim(sNewPart)) > 0 Then
        If Len(Trim(sOrig)) > 0 Then ConcatStr = Trim(sOrig) & sSeperator
        ConcatStr = ConcatStr & Trim(sNewPart)
    Else
        ConcatStr = sOrig
    End If

End Function
Private Function IsFunction(sExpr As String, Optional ByRef sFunctionName As String) As Boolean

    Dim nStart As Long, nStop As Long
    Dim sTemp As String
    Dim i As Long

    IsFunction = False

    'Lets decide if this is a function
    If InStr(1, sExpr, "(") > 1 Then
        nStop = InStr(1, sExpr, "(")
            
        nStart = InStrRev(sExpr, " ", nStop)
        For i = nStop - 1 To Max(nStart, 1) Step -1
            If IsAlpha(Mid(sExpr, i, 1)) = False And IsNumeric(Mid(sExpr, i, 1)) = False And Not Mid(sExpr, i, 1) = "_" And Not Mid(sExpr, i, 1) = "." Then
                If i > 1 Then
                    If IsAlpha(Mid(sExpr, i, 1)) = False And IsNumeric(Mid(sExpr, i, 1)) = False And Not Mid(sExpr, i - 1, 2) = "->" And Not Mid(sExpr, i, 2) = "->" Then
                        nStart = i
                        Exit For
                    End If
                Else
                    nStart = i
                    Exit For
                End If
            End If
        Next i
        
        sFunctionName = Mid(sExpr, nStart + 1, nStop - nStart - 1)
        'Is it?
        If Len(sFunctionName) > 0 Then
            sTemp = SearchAndReplace(sFunctionName, "_", "")
            sTemp = SearchAndReplace(sTemp, ".", "")
            sTemp = SearchAndReplace(sTemp, "->", "")
            sTemp = SearchAndReplace(sTemp, "0", "")
            sTemp = SearchAndReplace(sTemp, "1", "")
            sTemp = SearchAndReplace(sTemp, "2", "")
            sTemp = SearchAndReplace(sTemp, "3", "")
            sTemp = SearchAndReplace(sTemp, "4", "")
            sTemp = SearchAndReplace(sTemp, "5", "")
            sTemp = SearchAndReplace(sTemp, "6", "")
            sTemp = SearchAndReplace(sTemp, "7", "")
            sTemp = SearchAndReplace(sTemp, "8", "")
            sTemp = SearchAndReplace(sTemp, "9", "")
            IsFunction = IsAlpha(sTemp)
        End If
    End If
    
End Function
Private Function GetVariableDiff(sRetVal As String, oParent As clsFOTAArray, oVariables As clsFOTAArray, bPassVariablesUp As Boolean, bBySETVARIABLE As Boolean) As String

    Dim nP As Long, nV As Long
    Dim sKey As String, sText As String
    Dim sAdd As String
    
    For nP = 1 To oParent.Count
        sKey = oParent.GetKeyForIndex(nP)
        If oVariables.KeyInList(sKey) = True Then
            sText = oVariables.Text(sKey)
            oParent.Text(sKey) = sText
            Call oVariables.Remove(sKey)
            nP = nP - 1
        End If
    Next nP
    
    If bPassVariablesUp = True Then
        sAdd = ""
        For nV = 1 To oVariables.Count
            sKey = oVariables.GetKeyForIndex(nV)
            If Len(sKey) > 0 Then
            
                If bBySETVARIABLE = True Then
                    If Len(sAdd) > 0 Then
                        sAdd = sAdd & ":"
                    End If
                    sAdd = sAdd & "SetVariable(""" & sKey & """," & oVariables.GetTextForKey(sKey) & ")"
                Else
                    Call oParent.Add(oVariables.GetTextForKey(sKey), sKey)
                End If
            End If
        Next nV
        
        If bBySETVARIABLE = True Then
            GetVariableDiff = sAdd
            If Len(sRetVal) > 0 And Len(sAdd) > 0 Then
                GetVariableDiff = GetVariableDiff & ":"
            End If
        End If
    End If

    GetVariableDiff = GetVariableDiff & sRetVal

End Function

Private Function GetVariable(oVariables As clsFOTAArray, sVariable As String, ByRef sValue As String) As Boolean

    Dim sName As String
    
    sName = SearchAndReplace(sVariable, vbCrLf, "")
    sName = SearchAndReplace(sName, vbCr, "")
    sName = SearchAndReplace(sName, vbLf, "")
    sName = Trim(sName)
    
    If oVariables.KeyInList(sName) = True Then
        sValue = oVariables.GetTextForKey(sName)
        GetVariable = True
    Else
        GetVariable = False
    End If
    
End Function

Private Sub SetVariable(oVariables As clsFOTAArray, sVariable As String, sValue As String)

    Dim nIndex As Long
    Dim sName As String
    
    sName = SearchAndReplace(sVariable, vbCrLf, "")
    sName = SearchAndReplace(sName, vbCr, "")
    sName = SearchAndReplace(sName, vbLf, "")
    sName = Trim(sName)
    
    nIndex = oVariables.GetIndexForKey(sName)
    If Not nIndex = -1 Then
        Call oVariables.Remove(sName)
    End If
    Call oVariables.Add(sValue, sName)
        
End Sub
Private Function FixStrings(sExpression As String) As String

    Dim i As Long
    Dim nCnt As Long
    
    For i = 1 To Len(sExpression)
        
        If nCnt = 0 And Mid(sExpression, i, 1) = """" Then
            nCnt = 1
            FixStrings = FixStrings & """"
        ElseIf nCnt = 1 And Mid(sExpression, i, 2) = """""" Then
            FixStrings = FixStrings & m_cOP_QUOTE
            i = i + 1
        ElseIf nCnt = 1 And UCase(Mid(sExpression, i, 2)) = "OR" Then
            FixStrings = FixStrings & m_cOP_OR & Mid(sExpression, i, 1) & "_" & Mid(sExpression, i + 1, 1) & m_cOP_END
            i = i + 1
        ElseIf nCnt = 1 And UCase(Mid(sExpression, i, 3)) = "AND" Then
            FixStrings = FixStrings & m_cOP_AND & Mid(sExpression, i, 1) & "_" & Mid(sExpression, i + 1, 1) & "_" & Mid(sExpression, i + 2, 1) & m_cOP_END
            i = i + 2
        ElseIf nCnt = 1 And Mid(sExpression, i, 1) = """" Then
            nCnt = 0
            FixStrings = FixStrings & """"
        ElseIf nCnt = 1 And Mid(sExpression, i, 1) = ":" Then
            FixStrings = FixStrings & m_cOP_COLON
        ElseIf nCnt = 1 And Mid(sExpression, i, 1) = "=" Then
            FixStrings = FixStrings & m_cOP_EQUALS
        ElseIf nCnt = 1 And Mid(sExpression, i, 1) = "==" Then
            FixStrings = FixStrings & m_cOP_EQUALSEQUALS
        ElseIf nCnt = 1 And Mid(sExpression, i, 1) = "+" Then
            FixStrings = FixStrings & m_cOP_PLUS
        ElseIf nCnt = 1 And Mid(sExpression, i, 1) = "-" Then
            FixStrings = FixStrings & m_cOP_MINUS
        ElseIf nCnt = 1 And Mid(sExpression, i, 1) = "*" Then
            FixStrings = FixStrings & m_cOP_MULTIPLY
        ElseIf nCnt = 1 And Mid(sExpression, i, 1) = "/" Then
            FixStrings = FixStrings & m_cOP_DIVIDE
        ElseIf nCnt = 1 And Mid(sExpression, i, 1) = "(" Then
            FixStrings = FixStrings & m_cOP_LPAREN
        ElseIf nCnt = 1 And Mid(sExpression, i, 1) = ")" Then
            FixStrings = FixStrings & m_cOP_RPAREN
        ElseIf nCnt = 1 And Mid(sExpression, i, 1) = "'" Then
            FixStrings = FixStrings & m_cOP_TICK
        ElseIf nCnt = 1 And Mid(sExpression, i, 1) = ">" Then
            FixStrings = FixStrings & m_cOP_GREATERTHAN
        ElseIf nCnt = 1 And Mid(sExpression, i, 1) = "<" Then
            FixStrings = FixStrings & m_cOP_LESSTHAN
        ElseIf nCnt = 1 And Mid(sExpression, i, 1) = vbCr Then
            FixStrings = FixStrings & m_cOP_CR
        ElseIf nCnt = 1 And Mid(sExpression, i, 1) = vbLf Then
            FixStrings = FixStrings & m_cOP_LF
        ElseIf nCnt = 1 And Mid(sExpression, i, 1) = "&" Then
            FixStrings = FixStrings & m_cOP_ANDSIGN
        Else
            FixStrings = FixStrings & Mid(sExpression, i, 1)
        End If
        
    Next i

    FixStrings = SearchAndReplace(FixStrings, Chr(9), "        ")

End Function

Private Function UnFixStrings(sExpression As String) As String
    
    Dim nWhere As Long
    
    UnFixStrings = sExpression
    UnFixStrings = SearchAndReplace(UnFixStrings, m_cOP_QUOTE, """""")
    UnFixStrings = SearchAndReplace(UnFixStrings, m_cOP_EQUALS, "=")
    UnFixStrings = SearchAndReplace(UnFixStrings, m_cOP_EQUALSEQUALS, "==")
    UnFixStrings = SearchAndReplace(UnFixStrings, m_cOP_PLUS, "+")
    UnFixStrings = SearchAndReplace(UnFixStrings, m_cOP_MINUS, "-")
    UnFixStrings = SearchAndReplace(UnFixStrings, m_cOP_MULTIPLY, "*")
    UnFixStrings = SearchAndReplace(UnFixStrings, m_cOP_DIVIDE, "/")
    UnFixStrings = SearchAndReplace(UnFixStrings, m_cOP_LPAREN, "(")
    UnFixStrings = SearchAndReplace(UnFixStrings, m_cOP_RPAREN, ")")
    UnFixStrings = SearchAndReplace(UnFixStrings, m_cOP_COLON, ":")
    UnFixStrings = SearchAndReplace(UnFixStrings, m_cOP_TICK, "'")
    UnFixStrings = SearchAndReplace(UnFixStrings, m_cOP_GREATERTHAN, ">")
    UnFixStrings = SearchAndReplace(UnFixStrings, m_cOP_LESSTHAN, "<")
    UnFixStrings = SearchAndReplace(UnFixStrings, m_cOP_CR, vbCr)
    UnFixStrings = SearchAndReplace(UnFixStrings, m_cOP_LF, vbLf)
    UnFixStrings = SearchAndReplace(UnFixStrings, m_cOP_ANDSIGN, "&")

    While InStr(1, UnFixStrings, m_cOP_OR) > 0
        nWhere = InStr(1, UnFixStrings, m_cOP_OR)
        UnFixStrings = Left(UnFixStrings, nWhere - 1) & _
                       Mid(UnFixStrings, nWhere + 6, 1) & _
                       Mid(UnFixStrings, nWhere + 8, 1) & _
                       Mid(UnFixStrings, nWhere + 11)
    Wend
    
    While InStr(1, UnFixStrings, m_cOP_AND) > 0
        nWhere = InStr(1, UnFixStrings, m_cOP_AND)
        UnFixStrings = Left(UnFixStrings, nWhere - 1) & _
                       Mid(UnFixStrings, nWhere + 8, 1) & _
                       Mid(UnFixStrings, nWhere + 10, 1) & _
                       Mid(UnFixStrings, nWhere + 12, 1) & _
                       Mid(UnFixStrings, nWhere + 15)
    Wend
    
End Function

Private Function GetAllEncompasingQuotes(sExpr As String) As String

    Dim nStart As Long
    Dim nStop As Long
    
    If Left(Trim(sExpr), 1) = """" And Right(Trim(sExpr), 1) = """" Then
        nStart = InStr(1, sExpr, """")
    
        If nStart = 0 And nStop = 0 Then
            GetAllEncompasingQuotes = sExpr
        Else
            nStop = InStr(nStart + 1, sExpr, """")
            
            If nStop = 0 Then
                Call ErrorOccured(sExpr, m_nERROR_MatchingQuoteNotFound, m_cERROR_MatchingQuoteNotFound & " <" & sExpr & ">")
            Else
                GetAllEncompasingQuotes = Mid(sExpr, nStart + 1, nStop - nStart - 1)
            End If
        End If
    Else
        GetAllEncompasingQuotes = sExpr
    End If
    
End Function

Private Function SmartParseArry(sStringToParse As String, saParseBy() As String, Optional ByRef nNumberFound As Long) As String()

    Dim vaTemp() As String
    Dim nFound As Long: nFound = 0
    Dim nLastFoundPos As Long: nLastFoundPos = 1
    Dim nPos As Long: nPos = 1
    Dim nCharPos As Long, nQuotePos As Long, nOParenPos As Long, nCParenPos As Long
    Dim nCharCnt As Long, nQuoteCnt As Long, nParenCnt As Long
    Dim nTemp As Long
    Dim nLenOfItemFound As Long
    Dim i As Long
    nCharCnt = 0
    nQuoteCnt = 0
    nParenCnt = 0
        
    nCharPos = 0
    nLenOfItemFound = 0
    For i = 0 To UBound(saParseBy)
        If saParseBy(i) = """" Or saParseBy(i) = "(" Or saParseBy(i) = ")" Then
            Call ErrorOccured(sStringToParse, m_nERROR_InvalidParsingCharacter, m_cERROR_InvalidParsingCharacter & " <" & saParseBy(i) & ">")
        Else
            nTemp = InStr(nPos, sStringToParse, saParseBy(i))
            If nTemp > 0 And (nCharPos > nTemp Or nCharPos = 0) Then
                nCharPos = nTemp
                nLenOfItemFound = Len(saParseBy(i))
            End If
        End If
    Next i
    nQuotePos = InStr(nPos, sStringToParse, """")
    nOParenPos = InStr(nPos, sStringToParse, "(")
    nCParenPos = InStr(nPos, sStringToParse, ")")

    While nCharPos > 0 Or nParenCnt > 0 Or nQuoteCnt > 0

        If nQuoteCnt > 0 Then
            If nQuotePos > 0 Then
                nQuoteCnt = nQuoteCnt - 1
                nPos = nQuotePos + 1
            Else
                Call ErrorOccured(sStringToParse, m_nERROR_MatchingQuoteNotFound, m_cERROR_MatchingQuoteNotFound & " <" & sStringToParse & ">")
            End If
        ElseIf nCParenPos > 0 And (nCParenPos < nQuotePos Or nQuotePos = 0) And (nCParenPos < nCharPos Or nCharPos = 0 Or nParenCnt > 0) And (nCParenPos < nOParenPos Or nOParenPos = 0) Then
            nParenCnt = nParenCnt - 1
            nPos = nCParenPos + 1
        ElseIf nOParenPos > 0 And (nOParenPos < nQuotePos Or nQuotePos = 0) And (nOParenPos < nCharPos Or nCharPos = 0 Or nParenCnt > 0) Then
            nParenCnt = nParenCnt + 1
            nPos = nOParenPos + 1
        ElseIf nQuotePos > 0 And (nQuotePos < nCharPos Or nCharPos = 0 Or nParenCnt > 0) Then
            nQuoteCnt = nQuoteCnt + 1
            nPos = nQuotePos + 1
        Else
            nFound = nFound + 1
            ReDim Preserve vaTemp(1 To nFound) As String
            vaTemp(nFound) = Mid(sStringToParse, nLastFoundPos, nCharPos - nLastFoundPos)
            nPos = nCharPos + nLenOfItemFound
            nLastFoundPos = nPos
        End If
    
        nCharPos = 0
        nLenOfItemFound = 0
        For i = 0 To UBound(saParseBy)
            nTemp = InStr(nPos, sStringToParse, saParseBy(i))
            If nTemp > 0 And (nCharPos > nTemp Or nCharPos = 0) Then
                nCharPos = nTemp
                nLenOfItemFound = Len(saParseBy(i))
            End If
        Next i
        nQuotePos = InStr(nPos, sStringToParse, """")
        nOParenPos = InStr(nPos, sStringToParse, "(")
        nCParenPos = InStr(nPos, sStringToParse, ")")
    Wend
    
    If nParenCnt > 0 Then
        Call ErrorOccured(sStringToParse, m_nERROR_MatchingQuoteNotFound, m_cERROR_MatchingQuoteNotFound & " <" & sStringToParse & ">")
    End If
    
    nFound = nFound + 1
    ReDim Preserve vaTemp(1 To nFound) As String
    vaTemp(nFound) = Mid(sStringToParse, nLastFoundPos)
    nPos = nCharPos + 1
    nLastFoundPos = nPos
    
    SmartParseArry = vaTemp
    nNumberFound = nFound
    
End Function

Private Function SmartParse(sStringToParse As String, sParseBy As String, Optional ByRef nNumberFound As Long) As String()

    Dim saParseBy(0 To 0) As String
    
    saParseBy(0) = sParseBy
    SmartParse = SmartParseArry(sStringToParse, saParseBy, nNumberFound)

End Function

Private Function GetAllEncompasingParens(sExpr As String, Optional ByRef sCutOutExpr As String) As String
    
    Dim nT1 As Long, nT2 As Long
    Dim nPos As Long
    Dim sExpr1 As String
    Dim bDone As Boolean
    Dim nCount As Long
    Dim nStart As Long
    
    nStart = 0
    nPos = 1
    nCount = 0
    bDone = False
    sExpr1 = Trim(sExpr)
           
    While bDone = False
    
        nT1 = InStr(nPos, sExpr1, "(")
        nT2 = InStr(nPos, sExpr1, ")")
        
        'No closing parens
        If nT2 = 0 Then
            Call ErrorOccured(sExpr, m_nERROR_MatchingParenNotFound, m_cERROR_MatchingParenNotFound & " <" & sExpr & ">")
            bDone = True
            GetAllEncompasingParens = ""
            sCutOutExpr = ""
        'Found an opening bracket
        ElseIf nT1 < nT2 And nT1 > 0 Then
            If nStart = 0 Then nStart = nT1
            nCount = nCount + 1
            nPos = nT1 + 1
        'Found a closing bracket
        ElseIf nT2 < nT1 Or nT1 = 0 Then
            nCount = nCount - 1
            nPos = nT2 + 1
            If nCount = 0 Then
                GetAllEncompasingParens = Mid(sExpr1, nStart + 1, nPos - nStart - 2)
                sCutOutExpr = Left(sExpr1, nStart - 1) & m_cEXPR & Mid(sExpr1, nPos)
                bDone = True
            End If
        End If
    Wend
    
End Function

Public Function RunningInstancesCount() As Long
    RunningInstancesCount = m_oInstances.Count()
End Function

Public Function UnitTestFI(Optional ByRef sErrors As String = "") As Boolean

    Dim oFunction As clsFI_Function

    Set oFunction = New clsFI_Function
        Call oFunction.AddLine("Testing( sValue )")
        Call oFunction.AddLine("Return( ""Or"" )")
    Call AddFunction(oFunction)

    Set oFunction = New clsFI_Function
        Call oFunction.AddLine("TruthTest( nValue )")
        Call oFunction.AddLine("nTest = 1")
        Call oFunction.AddLine("nTest = nTest + 1")
        Call oFunction.AddLine("Return( nTest == nValue )")
    Call AddFunction(oFunction)
    
    Set oFunction = New clsFI_Function
        Call oFunction.AddLine("No_Param_Test(  )")
        Call oFunction.AddLine("Return( ""Kewl"" )")
    Call AddFunction(oFunction)

    Set oFunction = New clsFI_Function
        Call oFunction.AddLine("FunnyName->Test(  )")
        Call oFunction.AddLine("Return( ""Kewl"" )")
    Call AddFunction(oFunction)

    Set oFunction = New clsFI_Function
        Call oFunction.AddLine("FunnyName.Test(  )")
        Call oFunction.AddLine("Return( ""Kewl"" )")
    Call AddFunction(oFunction)

    Set oFunction = New clsFI_Function
        Call oFunction.AddLine("NumberName.Test1(  )")
        Call oFunction.AddLine("Return( ""Kewl"" )")
    Call AddFunction(oFunction)

    Set oFunction = New clsFI_Function
        Call oFunction.AddLine("0Number1Name2.Test3(  )")
        Call oFunction.AddLine("Return( ""Kewl"" )")
    Call AddFunction(oFunction)

    Set oFunction = New clsFI_Function
        Call oFunction.AddLine("0Number1Name2Test3(  )")
        Call oFunction.AddLine("Return( ""Kewl"" )")
    Call AddFunction(oFunction)

Call Test(sErrors, "Static nCnt;", "")

    Call Test(sErrors, "-1E-19", "-0.0000000000000000001")
    Call Test(sErrors, "-1E+19", "-10,000,000,000,000,000,000")
    Call Test(sErrors, "-1.234E+1", "-12.34")
    Call Test(sErrors, "-12.34E+1", "-123.4")
    Call Test(sErrors, "-12.34E-1", "-1.234")
    Call Test(sErrors, "-12.34E+10", "-123,400,000,000")
    Call Test(sErrors, "-12.34E-10", "-0.000000001234")
    Call Test(sErrors, "IF(-19==1,1,2)", 2)
    Call Test(sErrors, "IF(-1E-19==1,1,2)", 2)
    Call Test(sErrors, "-1E-1-.1", -0.2)
    Call Test(sErrors, "True  AND True ==  TRue", True)
    Call Test(sErrors, "sD = ""Spell"":bT = True:sD == ""Spell"" AND bT == TRue", True)
    Call Test(sErrors, vbCrLf + """a"" + ""b""", "ab")
    Call Test(sErrors, """2"" + ""A"" AND TRUE", False)
    Call Test(sErrors, "(1+(((1)))) + ""A"" AND TRUE", False)
    Call Test(sErrors, "sDesc = ""="":sDesc", "=")
    Call Test(sErrors, "Floor(5)", "5")
    Call Test(sErrors, "Floor(5.1)", "5")
    Call Test(sErrors, "Floor(5.9)", "5")
    Call Test(sErrors, "'Calculate The Damage:" & vbCrLf & "nDamage = 0:" & vbCrLf & "nLevel = Min(CurrentChar.Level1,10):" & vbCrLf & "For(i =0,i<nLevel,i=i+1," & vbCrLf & "      nDamage = nDamage + 5):" & vbCrLf & vbCrLf & "'Prepare our description:" & vbCrLf & "sDesc = CurrentChar.Name + "" holds a tiny ball " & vbCrLf & "of bat guano and sulphur in their hand and points.  A streak of light flys in that direction and with a low roar a fireball blossoms into a 20 foot radius.  (Those within the 20 feet take "" + nDamage + "" dmg.  Make a save vs. spell and take "" + Floor(nDamage/2) + "" dmg)"":" & vbCrLf & vbCrLf & "'Now send it off!:" & vbCrLf & "Send(sDesc)", "Trae Vance holds a tiny ball " & vbCrLf & "of bat guano and sulphur in their hand and points.  A streak of light flys in that direction and with a low roar a fireball blossoms into a 20 foot radius.  (Those within the 20 feet take 35 dmg.  Make a save vs. spell and take 17 dmg)")
    Call Test(sErrors, """Hi" & vbCrLf & "Bye""", "Hi" & vbCrLf & "Bye")
    Call Test(sErrors, "nTest = 10:" & vbCrLf & vbCr & vbLf & "nTest", 10)
    Call Test(sErrors, "        nTest        =        0        :        While(        nTest        <        5        ,        nTest        =        nTest        +        1        )        :        nTest        ", 5)
    Call Test(sErrors, "nTest=0:While(nTest<5,nTest=nTest+1):nTest", 5)
    Call Test(sErrors, "Testing(""JoeOrSam"")", "Or")
    Call Test(sErrors, "No_Param_Test() + "" Man""", "Kewl Man")
    Call Test(sErrors, "nTest=0'Declare:nTest", "0")
    Call Test(sErrors, "nTest=0'Declare:nTest'Testing", "0")
    Call Test(sErrors, "'Bogus nTest=0'Declare:nTest", "nTest")
    Call Test(sErrors, "STAtic sName = ""Joe"":r=0:r", "0")
    Call Test(sErrors, "sName", "Joe")
    Call Test(sErrors, "sName = ""Sam""", "")
    Call Test(sErrors, "sName", "Sam")
    Call Test(sErrors, "chr(65)+chr(43) == ""A+""", True)
    Call Test(sErrors, """What do or and """, "What do or and ")
    Call Test(sErrors, "trim(""2-1"" + "" "")", "2-1")
    Call Test(sErrors, "IF(((1==1)),1+1,2+2)", 2)
    Call Test(sErrors, "nValue=0:FOr(i=1,i<5,i=i+1,IF(i<3,nValue = nValue+1,)):nValue", "2")
    Call Test(sErrors, "nValue=0:FOr(i=1,i<5,i=i+1,nValue = nValue+1):nValue", "4")
    Call Test(sErrors, "IF(true,1+1,2+2)", 2)
    Call Test(sErrors, "IIF(true,1+1,2+2)", 2)
    Call Test(sErrors, "10.01 + 1.93", 11.94)
    Call Test(sErrors, "(nValue = TruthTest(2):True == nValue):True == nValue", "(nValue = TruthTest(2):True == nValue):True == nValue", 0)
    Call Test(sErrors, "(nValue = TruthTest(2):True == nValue):True == nValue", "(nValue = (SetVariable(""nValue"",2):nTest = 1:nTest = nTest + 1:Return( nTest == nValue )):True == nValue):True == nValue", 1)
    Call Test(sErrors, "(nValue = TruthTest(2):True == nValue):True == nValue", "(nValue = (SetVariable(""nValue"",2):nTest = 1:nTest = nTest + 1:Return( nTest == nValue )):True == nValue):True == nValue", 1)
    Call Test(sErrors, "(nValue = (SetVariable(""nValue"",2):nTest = 1:nTest = nTest + 1:Return( nTest == nValue )):True == nValue):True == nValue", "(nValue = (SetVariable(""nValue"",2):SetVariable(""nTest"", 1):nTest = nTest + 1:Return( nTest == nValue )):True == nValue):True == nValue", 1)
    Call Test(sErrors, "(nValue = (SetVariable(""nValue"",2):SetVariable(""nTest"", 1):nTest = nTest + 1:Return( nTest == nValue )):True == nValue):True == nValue", "(nValue = (SetVariable(""nValue"",2):SetVariable(""nTest"", 1):nTest =1 + 1:Return( nTest == nValue )):True == nValue):True == nValue", 1)
    Call Test(sErrors, "(nValue = (SetVariable(""nValue"",2):SetVariable(""nTest"", 1):nTest = 1+ 1:Return( nTest == nValue )):True == nValue):True == nValue", "(nValue = (SetVariable(""nValue"",2):SetVariable(""nTest"", 1):nTest =2:Return( nTest == nValue )):True == nValue):True == nValue", 1)
    Call Test(sErrors, "(nValue = (SetVariable(""nValue"",2):SetVariable(""nTest"", 1):nTest =2:Return( nTest == nValue )):True == nValue):True == nValue", "(nValue = (SetVariable(""nValue"",2):SetVariable(""nTest"",2):Return( nTest == nValue )):True == nValue):True == nValue", 1)
    Call Test(sErrors, "(nValue = (SetVariable(""nValue"",2):SetVariable(""nTest"",2):Return( nTest == nValue )):True == nValue):True == nValue", "(nValue = (SetVariable(""nValue"",2):SetVariable(""nTest"",2):Return(2 == nValue)):True == nValue):True == nValue", 1)
    Call Test(sErrors, "(nValue = (SetVariable(""nValue"",2):SetVariable(""nTest"",2):Return(2== nValue )):True == nValue):True == nValue", "(nValue = (SetVariable(""nValue"",2):SetVariable(""nTest"",2):Return(2==2)):True == nValue):True == nValue", 1)
    Call Test(sErrors, "(nValue = (SetVariable(""nValue"",2):SetVariable(""nTest"",2):Return(2==2)):True == nValue):True == nValue", "(nValue = (SetVariable(""nValue"",2):SetVariable(""nTest"",2):Return(True)):True == nValue):True == nValue", 1)
    Call Test(sErrors, "(nValue = (SetVariable(""nValue"",2):SetVariable(""nTest"",2):Return(True)):True == nValue):True == nValue", "(nValue = (SetVariable(""nValue"",2):SetVariable(""nTest"",2):True):True == nValue):True == nValue", 1)
    Call Test(sErrors, "(nValue = (SetVariable(""nValue"",2):SetVariable(""nTest"",2):True):True == nValue):True == nValue", "(nValue = True:True == nValue):True == nValue", 1)
    Call Test(sErrors, "(nValue = True:True == nValue):True == nValue", "(SetVariable(""nValue"", True):True == nValue):True == nValue", 1)
    Call Test(sErrors, "(SetVariable(""nValue"", True):True == nValue):True == nValue", "(SetVariable(""nValue"", True):True == True):True == nValue", 1)
    Call Test(sErrors, "(SetVariable(""nValue"", True):True == True):True == nValue", "(SetVariable(""nValue"", True):True):True == nValue", 1)
    Call Test(sErrors, "(SetVariable(""nValue"", True):True):True == nValue", "True:True == nValue", 1)
    Call Test(sErrors, "SetVariable(""nValue"", True):True:True == True", "SetVariable(""nValue"", True):True:True", 1)
    Call Test(sErrors, "SetVariable(""nValue"", True):True:True ", "True:True", 1)
    Call Test(sErrors, "nValue = TruthTest(2)", "")
    Call Test(sErrors, "nValue = True", "")
    Call Test(sErrors, "(nValue = TruthTest(2):True == nValue):True == nValue", "True:False")
    Call Test(sErrors, "2 == nValue:nValue = TruthTest(2):True == nValue", "False:True")
    Call Test(sErrors, "(nValue = (true):True == nValue):True == nValue", "True:False")
    Call Test(sErrors, "(nValue =  true :True == nValue):True == nValue", "True:False")
    Call Test(sErrors, "truthtest(2)", True)
    Call Test(sErrors, "2 == nValue:TruthTest(2)", "False:True")
    Call Test(sErrors, "TruthTest(2)", True)
    Call Test(sErrors, "TruthTest(3)", False)
    Call Test(sErrors, "1 == 1:2 == 2", "True:True")
    Call Test(sErrors, "(nTest = 1+1:(2==nTest)) == True", True)
    Call Test(sErrors, "(nTest = 1+1:(2==nTest)) == True", "(nTest = 1+1:(2==nTest)) == True", 0)
    Call Test(sErrors, "(nTest = 1+1:(2==nTest)) == True", "(nTest =2:(2==nTest)) == True", 1)
    Call Test(sErrors, "(nTest =2:(2==nTest)) == True", "(SetVariable(""nTest"",2):(2==nTest)) == True", 1)
    Call Test(sErrors, "SetVariable(""nTest"",2):((2==nTest)) == True", "SetVariable(""nTest"",2):((2==2)) == True", 1)
    Call Test(sErrors, "SetVariable(""nTest"",2):((2==2)) == True", "SetVariable(""nTest"",2):((True)) == True", 1)
    Call Test(sErrors, "SetVariable(""nTest"",2):((True)) == True", "SetVariable(""nTest"",2):(True) == True", 1)
    Call Test(sErrors, "SetVariable(""nTest"",2):(True) == True", "SetVariable(""nTest"",2):True == True", 1)
    Call Test(sErrors, "SetVariable(""nTest"",2):True == True", "SetVariable(""nTest"",2):True", 1)
    Call Test(sErrors, "SetVariable(""nTest"",2):True", "True", 1)
    Call Test(sErrors, "True", True, 1)
    Call Test(sErrors, "(nTest = 1+1:2==nTest) == True", True)
    Call Test(sErrors, "(nTest = 1+1:Return(2==nTest)) == True", True)
    Call Test(sErrors, "SetVariable(""hi"",3)", "")
    Call Test(sErrors, "nTest = 1+1:2 == nTest ", True)
    Call Test(sErrors, "nTest = 1+1", "")
    Call Test(sErrors, """AndOr""", "AndOr")
    Call Test(sErrors, "False AND TRUE", False)
    Call Test(sErrors, """Test:"" + "" [AND][OR] """, "Test: [AND][OR] ")
    Call Test(sErrors, "1<>2", True)
    Call Test(sErrors, "1<2", True)
    Call Test(sErrors, "1>2", False)
    Call Test(sErrors, "1-2+3", "-1 + 3", 1)
    Call Test(sErrors, "(1+2+3)", "(1+5)", 1)
    Call Test(sErrors, "(1+2+3)", "(6)", 2)
    Call Test(sErrors, "(1+2+3)", "6", 3)
    Call Test(sErrors, "(1+2+3)", "(1+5)", 1)
    Call Test(sErrors, "(1+5)", "(6)", 1)
    Call Test(sErrors, "(6)", "6", 1)
    Call Test(sErrors, "(1-2-3)", "(1-5)", 1)
    Call Test(sErrors, "(1-2-3)", "(-4)", 2)
    Call Test(sErrors, "(1-2-3)", "-4", 3)
    Call Test(sErrors, "(1-2-3)", "(1-5)", 1)
    Call Test(sErrors, "(1-5)", "(-4)", 1)
    Call Test(sErrors, "(-4)", "-4", 1)
    Call Test(sErrors, "((1))", "(1)", 1)
    Call Test(sErrors, "((1))", "1", 2)
    Call Test(sErrors, "(1+(((1)))) + ""A""", "(1+((1))) + ""A""", 1)
    Call Test(sErrors, "(1+(1)) + ""A""", "(2) + ""A""", 2)
    Call Test(sErrors, "(1+(((1)))) + ""A""", "(2) + ""A""", 4)
    Call Test(sErrors, "(2) + 1", "(2) + 1", 0)
    Call Test(sErrors, "(1+(((1)))) + ""A""", "(1+(((1)))) + ""A""", 0)
    Call Test(sErrors, "(1+(((1)))) + ""A""", "(1+((1))) + ""A""", 1)
    Call Test(sErrors, "(1+(((1)))) + ""A""", "(1+(1)) + ""A""", 2)
    Call Test(sErrors, "(1+(((1)))) + ""A""", "(1 + 1) + ""A""", 3)
    Call Test(sErrors, "(1+(((1)))) + ""A""", "(2) + ""A""", 4)
    Call Test(sErrors, "(1+(((1)))) + ""A""", "2 + ""A""", 5)
    Call Test(sErrors, "(1+(((1)))) + ""A""", "2A", 6)
    Call Test(sErrors, "(2) + 1", "2 + 1", 1)
    Call Test(sErrors, "(2) + 1", "3", 2)
    Call Test(sErrors, "Left(""Hello"",Instr(1,""Hello"",""ll"")-1)", "He")
    Call Test(sErrors, "Instr(1,""Hello"",""ll"")", 3)
    Call Test(sErrors, "Mid(""Hi"",1,1) + Mid(""Hi"",2,1) + ""!""", "Hi!")
    Call Test(sErrors, "Mid(""Test1"",5,1) + 1", "11")
    Call Test(sErrors, "Cint(Mid(""Test1"",5,1)) + 1", 2)
    Call Test(sErrors, "Cstr(1) + 1", "11")
    Call Test(sErrors, "(1+1)", "(1 + 1)", 0)
    Call Test(sErrors, "(1+1)", "(2)", 1)
    Call Test(sErrors, "(1+1)", "2", 2)
    Call Test(sErrors, "(1+1)", "2", 3)
    Call Test(sErrors, "(1)", 1)
    Call Test(sErrors, "(1-2-3-4)", -8)
    Call Test(sErrors, "(1-2-3-4)", "(1-2-3-4)", 0)
    Call Test(sErrors, "(1-2-3-4)", "(1-2-7)", 1)
    Call Test(sErrors, "(1-2-3-4)", "(1-9)", 2)
    Call Test(sErrors, "(1-2-3-4)", "(-8)", 3)
    Call Test(sErrors, "(1-2-3-4)", -8, 4)
    Call Test(sErrors, "(1/2/3/4)", 0.375)
    Call Test(sErrors, "(1/2/3/4)", "(1/2/3/4)", 0)
    Call Test(sErrors, "(1/2/3/4)", "(1/2/0.75)", 1)
    Call Test(sErrors, "(1/2/3/4)", "(1/2.66666666666667)", 2)
    Call Test(sErrors, "(1/2/3/4)", "(0.375)", 3)
    Call Test(sErrors, "(1/2/3/4)", 0.375, 4)
    Call Test(sErrors, "(1*2*3*4)", 24)
    Call Test(sErrors, "(1*2*3*4)", "(1*2*3*4)", 0)
    Call Test(sErrors, "(1*2*3*4)", "(1*2*12)", 1)
    Call Test(sErrors, "(1*2*3*4)", "(1*24)", 2)
    Call Test(sErrors, "(1*2*3*4)", "(24)", 3)
    Call Test(sErrors, "(1*2*3*4)", 24, 4)
    Call Test(sErrors, "(1+2+3+4)", 10)
    Call Test(sErrors, "(1+2+3+4)", "(1+2+3+4)", 0)
    Call Test(sErrors, "(1+2+3+4)", "(1+2+7)", 1)
    Call Test(sErrors, "(1+2+3+4)", "(1+9)", 2)
    Call Test(sErrors, "(1+2+3+4)", "(10)", 3)
    Call Test(sErrors, "(1+2+3+4)", 10, 4)
    Call Test(sErrors, "((1))-(2+3)", -4)
    Call Test(sErrors, "(1-1)", "(0)", 1)
    Call Test(sErrors, """Test#:"" + ""(2+(3))"" + "" Is Done""", "Test#:(2+(3)) Is Done")
    Call Test(sErrors, """Test#:"" + (2+(3)) + "" Is Done""", "Test#:5 Is Done")
    Call Test(sErrors, """Test#: "" + TRIm((2+(3))) + "" Is Done""", "Test#: 5 Is Done")
    Call Test(sErrors, "TrIM(UCASe("" hello "")) == ""HELLO""", True)
    Call Test(sErrors, "TrIM(UCASe("" hello ""))", "HELLO")
    Call Test(sErrors, "UCASe(""hello"")", "HELLO")
    Call Test(sErrors, "lCASe(""HeLLO"")", "hello")
    Call Test(sErrors, "((1))-2+3", 2)
    Call Test(sErrors, "((1))-(2+3)", -4)
    Call Test(sErrors, "FALSE == (FALSE)", True)
    Call Test(sErrors, "100/3 == ((51-(1)))", False)
    Call Test(sErrors, "-((1))", -1)
    Call Test(sErrors, "1 - 2 + 3", 2)
    Call Test(sErrors, "TRIm("" 100 "") ==  50*2", True)
    Call Test(sErrors, "1 - 2 + 3", 2)
    Call Test(sErrors, "1-2+3", 2)
    Call Test(sErrors, "((51-(1)))", "50")
    Call Test(sErrors, "1 == 1", True)
    Call Test(sErrors, "1-2+3", 2)
    Call Test(sErrors, "100/2 == ((51-(1)))", True)
    Call Test(sErrors, "1-1 == -1", False)
    Call Test(sErrors, "1-1 == 0", True)
    Call Test(sErrors, "-1 == 1", False)
    Call Test(sErrors, "((1-1)-1) == -1", True)
    Call Test(sErrors, "100", "100")
    Call Test(sErrors, "TRIm("" Hello "")", "Hello")
    Call Test(sErrors, """["" + TRIm("" Hello "") + ""]""", "[Hello]")
    Call Test(sErrors, "1 == 1", True)
    Call Test(sErrors, "FunnyName.Test() + "" Man""", "Kewl Man")
    Call Test(sErrors, "FunnyName->Test() + "" Man""", "Kewl Man")
    Call Test(sErrors, "NumberName.Test1() + "" Man""", "Kewl Man")
    Call Test(sErrors, "0Number1Name2.Test3() + "" Man""", "Kewl Man")
    Call Test(sErrors, "0Number1Name2Test3() + "" Man""", "Kewl Man")
    Call Test(sErrors, "(sDesc = ""Hi""):" & vbCrLf & "sDesc", "sDesc")
    Call Test(sErrors, "(sDesc = ""Hi"":):" & vbCrLf & "sDesc", "sDesc")
    Call Test(sErrors, "sDesc = """":" & vbCrLf & "(sDesc = ""Hi"":):" & vbCrLf & "sDesc", "Hi")

    UnitTestFI = (Len(sErrors) > 0)

End Function
Private Sub Test(ByRef sErrors As String, sTest As String, vAnswer As Variant, Optional nLines As Long = -1)

    Static nNum As Long
    nNum = nNum + 1

    Call RunTest(nNum, sErrors, sTest, vAnswer, nLines)
    If nLines = -1 Then
        Call ResumptionTest(nNum, sErrors, sTest, vAnswer)
        Call AdvanceTest(nNum, sErrors, sTest, vAnswer)
    End If

End Sub
Private Sub ResumptionTest(nNum As Long, sErrors As String, sTest As String, vAnswer As Variant)

    Dim i As Long
    Dim sTemp As String
    Dim bFinished As Boolean
    
    sTemp = sTest
    For i = 0 To 100
        sTemp = Run(sTemp, 1, bFinished)
        m_nTestCount = m_nTestCount + 1
        If bFinished = True Then
            If Not sTemp = vAnswer Then
                sErrors = sErrors & "Resumption Test(" & nNum & "): Failed.  " & _
                                    "[" & sTest & "] = " & "[" & sTemp & "]" & vbCrLf
            End If
            Exit Sub
        End If
    Next i

    sErrors = sErrors & "Resumption Test(" & nNum & "): Did not finish" & vbCrLf

End Sub
Private Sub RunTest(nNum As Long, ByRef sErrors As String, sTest As String, vAnswer As Variant, Optional nLines As Long = -1)

    Dim sAnsWeGot As String

    sAnsWeGot = Run(sTest, nLines)
    m_nTestCount = m_nTestCount + 1
    If Not SearchAndReplace(sAnsWeGot, " ", "") = SearchAndReplace(CStr(vAnswer), " ", "") Then
        sErrors = sErrors & "Test(" & nNum & "): Failed.  [" & sTest & "] = [" & sAnsWeGot & "]" & vbCrLf
    End If

End Sub
Private Sub AdvanceTest(nNum As Long, ByRef sErrors As String, sTest As String, vAnswer As Variant)

    Dim i As Long
    Dim nNumLines As Long
        
    If m_oInstances.Count > 0 Then
        Call Err.Raise(-1, "FOTAInterpeter.AdvanceTest", "Their are preexisting instances.  Unable to start AdvanceTest.")
    Else
        For nNumLines = 1 To 10
            Call SYSTEM_RunAndStore(sTest, False)
    
            While m_oInstances.Count > 0
                Set m_oAdvancingInstance = m_oInstances(1)
                
                If m_oAdvancingInstance.Advance(Me, nNumLines) = True Then
                    'Were done!
                    If Not m_oAdvancingInstance.Expression = vAnswer Then
                        sErrors = sErrors & "Advance Test(" & nNum & "): Failed.  [" & sTest & "] = [" & m_oAdvancingInstance.Expression & "]" & vbCrLf
                        nNumLines = 11
                    End If
                    Call StopInstance(m_oAdvancingInstance.GUID)
                End If
            Wend
        Next nNumLines
    End If
    
End Sub
Private Sub Class_Initialize()
    Call Randomize
    m_cLINETERMINATORS(0) = ":"
    m_cLINETERMINATORS(1) = ";"
    'm_cLINETERMINATORS(2) = vbCrLf
End Sub
Public Function GetRunningInstances() As Collection
    Set GetRunningInstances = m_oInstances
End Function
