home *** CD-ROM | disk | FTP | other *** search
/ Planet Source Code Jumbo …e CD Visual Basic 1 to 7 / 1_2002.ISO / Data / Zips / CODE_UPLOAD121.psc / CExpression.cls next >
Encoding:
Visual Basic class definition  |  1999-07-18  |  12.7 KB  |  409 lines

  1. VERSION 1.0 CLASS
  2. BEGIN
  3.   MultiUse = -1  'True
  4.   Persistable = 0  'NotPersistable
  5.   DataBindingBehavior = 0  'vbNone
  6.   DataSourceBehavior  = 0  'vbNone
  7.   MTSTransactionMode  = 0  'NotAnMTSObject
  8. END
  9. Attribute VB_Name = "CExpression"
  10. Attribute VB_GlobalNameSpace = False
  11. Attribute VB_Creatable = True
  12. Attribute VB_PredeclaredId = False
  13. Attribute VB_Exposed = False
  14. '--------------------------------------------------------------
  15. '  A class for compiling and evaluating expressions
  16. '  Author:  This code was originally obtained from VBPJ Journal
  17. '           from an article written by Fransisco Balena
  18. '           It was adapted and modified by the author to include
  19. '           additional functions, and improved handling of invalid
  20. '           calculations.
  21. '  Kevin Matney
  22. '  Date:        March 18, 1998
  23. '--------------------------------------------------------------
  24.  
  25. #Const SupportStrings = -1
  26.  
  27. Option Explicit
  28.  
  29. Public Enum expErrorCode
  30.     expOK = 0
  31.     expSyntaxError
  32.     expUnknownFunction
  33.     expUnknownOperator
  34.     expWrongNumberOfArguments
  35.     expInvalidInputforFunction
  36. End Enum
  37.  
  38. Private Enum expOpcodes
  39.     opUnknown               ' used for errors
  40.     opValue                 ' a constant or a variable
  41.     opStart                 ' special opcodes (operands)
  42.     opOpenBracket
  43.     opMinus                 ' unary opcodes
  44.     opNot
  45.     
  46.     opEnd                   ' special opcodes (operators)
  47.     opComma                 ' DO NOT alter this order!
  48.     opCloseBracket
  49.     
  50.     opFirst_BinaryOperator  ' binary opcodes (symbols)
  51.     opPower = opFirst_BinaryOperator
  52.     opMul
  53.     opDiv                   ' IMPORTANT: these opcodes must be in a
  54.     opIntDiv                ' sequence so that no opcode is a prefix
  55.     opAdd                   ' for another opcode that follows it
  56.     opSub                   ' (e.g. "<>" and "<=" must come before "<"
  57.     opEq                    '  and ">=" must come before ">"
  58.     opNe
  59.     opLe
  60.     opLt
  61.     opGe
  62.     opGt
  63.     opMod                   ' binary opcodes (alphabetic)
  64.     opAnd
  65.     opOr
  66.     opXor
  67. #If SupportStrings Then
  68.     opAppend
  69. #End If
  70.  
  71.     opFirst_Function        ' opcode of first function
  72.     opPi = opFirst_Function ' zero-argument functions
  73.     opDeg
  74.     opRad
  75.     opAbs                   ' one-argument functions
  76.     opInt
  77.     opFix
  78.     opSgn
  79.     opSqr
  80.     opLog
  81.     opLn
  82.     opExp
  83.     opSin
  84.     opAsin
  85.     opCos
  86.     opAcos
  87.     opTan
  88.     opAtn
  89.     opAtan
  90.     opSec
  91.     opCosec
  92.     opCotan  'add new functions here
  93.     opSin_D
  94.     opCos_D
  95.     opTan_D
  96.     
  97.     opPow
  98.     opMin                   ' two-argument functions
  99.     opMax
  100.     opIIf                   ' three-argument functions
  101.     
  102. #If SupportStrings Then
  103.     opLen                   ' one-argument string functions
  104.     opAsc
  105.     opSpace
  106.     opString                ' two-argument string functions
  107.     opLeft
  108.     opRight
  109.     opMid                   ' three-argument string functions
  110.     opInstr
  111. #End If
  112.     
  113.     opDummy
  114.     opLast_Opcode = opDummy - 1 ' last opcode used
  115. End Enum
  116.  
  117. ' max number of pending operators
  118. Const STACK_SIZE = 30
  119. ' max number of items in the expression
  120. Const MAX_ITEMS = 200
  121.  
  122. ' the Default value, returned if a runtime occurs
  123. Public DefaultValue As Variant
  124.  
  125. ' if True (default), runtime errors are raised using the Err.Raise VBA method
  126. ' if False, errors are notified to the calling program only through
  127. ' the Error* properties
  128. Public RaiseErrors As Boolean
  129.  
  130. ' if True (default), variables are created as needed
  131. ' if False, an error occurs if a variable is not declared in advance
  132. Public AutoCreateVariables As Boolean
  133.  
  134. ' member variables
  135. Private m_Expression As String
  136. Private m_ErrorCode As expErrorCode
  137. Private m_ErrorDescription As String
  138. Private m_ErrorPos As Long
  139.  
  140. ' the collection of variables
  141. Private m_Variables As Collection
  142.  
  143. ' the collection of roots
  144. Private m_Roots As Collection
  145.  
  146. ' these arrays hold information on all operands and functions
  147. Dim opNames(opLast_Opcode) As String
  148. Dim opPriority(opLast_Opcode) As Byte
  149. Dim opNumArgs(opLast_Opcode) As Integer
  150.  
  151. ' this holds the expression in compiled form
  152. Dim compItems As Long
  153. Dim compValues() As Variant
  154. Dim compOpcodes() As Integer
  155.  
  156. ' the expression to be evaluated
  157.  
  158. Property Get Expression() As String
  159.     Expression = m_Expression
  160. End Property
  161.  
  162. Property Let Expression(ByVal newValue As String)
  163.     m_Expression = newValue
  164.     ' compile the expression
  165.     CompileExpression
  166. End Property
  167.  
  168. ' information on the current error code
  169.  
  170. Property Get ErrorCode() As expErrorCode
  171.     ErrorCode = m_ErrorCode
  172. End Property
  173.  
  174. Property Get ErrorDescription() As String
  175.     ErrorDescription = m_ErrorDescription
  176. End Property
  177.  
  178. Property Get ErrorPos() As Long
  179.     ErrorPos = m_ErrorPos
  180. End Property
  181.  
  182. ' clear the error code
  183.  
  184. Sub ClearError()
  185.     m_ErrorCode = expOK
  186.     m_ErrorDescription = ""
  187.     m_ErrorPos = 0
  188. End Sub
  189.  
  190. ' access to the variables
  191.  
  192. Function Variable(varName As Variant, Optional createIfNeeded As Boolean) As CVariable
  193.     On Error Resume Next
  194.     If IsNumeric(varName) Then
  195.         Set Variable = m_Variables(varName)
  196.     Else
  197.         Set Variable = m_Variables(UCase$(varName))
  198.         If Err > 0 And createIfNeeded Then
  199.             Err = 0
  200.             ' if it doesn't exist, create it if requested
  201.             Dim newVar As New CVariable
  202.             newVar.Name = varName
  203.             ' add to the collection of variables
  204.             AddVariable newVar
  205.             Set Variable = newVar
  206.         End If
  207.     End If
  208. End Function
  209.  
  210. Function AddVariable(newVar As CVariable) As Long
  211.     ' add a new variable to the collection of variables
  212.     ' recognized by this function, returns its index in the collection
  213.     Dim ucaseName As String
  214.     Dim Index As Integer
  215.     
  216.     On Error Resume Next
  217.  
  218.     ucaseName = UCase$(newVar.Name)
  219.     
  220.     ' add to the collection of variables
  221.     ' this collection is always sorted on variable name
  222.     m_Variables.Remove ucaseName
  223.     Err = 0
  224.     
  225.     For Index = 1 To m_Variables.Count
  226.         If UCase$(m_Variables(Index).Name) > ucaseName Then
  227.             m_Variables.Add newVar, ucaseName, Index
  228.             AddVariable = Index
  229.             Exit Function
  230.         End If
  231.     Next
  232.             
  233.     ' add to the end of the collection
  234.     m_Variables.Add newVar, ucaseName
  235.     AddVariable = m_Variables.Count
  236.             
  237. End Function
  238.  
  239.  
  240. Function VariablesCount() As Long
  241.     VariablesCount = m_Variables.Count
  242. End Function
  243.  
  244.  
  245.  
  246.  
  247.  
  248. ' compile the expression (private)
  249.  
  250. Private Sub CompileExpression()
  251.     Dim expr As String
  252.     Dim Index As Long
  253.     Dim sp As Integer
  254.     Dim opSp As Integer
  255.     Dim argSp As Integer
  256.     Dim waitForOperator As Boolean
  257.     Dim temp As Variant
  258.     Dim opcode As Integer
  259.     Dim newVar As CVariable
  260.     
  261.     ' reset the compiled expression and the roots
  262.     compItems = 0
  263.     ReDim compOpcodes(MAX_ITEMS) As Integer
  264.     ReDim compValues(MAX_ITEMS) As Variant
  265.     Set m_Roots = New Collection
  266.     
  267.     ' these are the temporary stacks used for parsing
  268.     Dim opStack(STACK_SIZE) As Integer
  269.     Dim argStack(STACK_SIZE) As Integer
  270.     
  271.     ' reset error codes
  272.     m_ErrorCode = expOK
  273.     m_ErrorDescription = ""
  274.  
  275.     ' add a trailing char to avoid errors and signal the expression end
  276.     expr = m_Expression + opNames(opEnd)
  277.     ' start with the highest priority
  278.     opcode = opStart
  279.     GoSub CompileExprPushOpcode
  280.     Index = 1
  281.  
  282.     ' main compilation loop
  283.  
  284.     Do
  285.         SkipBlanks expr, Index
  286.         m_ErrorPos = Index
  287.     
  288.         If waitForOperator = False Then
  289.     
  290.             Select Case Mid$(expr, Index, 1)
  291.             Case "0" To "9", "."
  292.                 ' found a numeric constant
  293.                 temp = GetNumber(expr, Index)
  294.                 If opStack(opSp) = opMinus Then
  295.                     ' if there is an unary minus on the operator stack
  296.                     ' this is a negative number
  297.                     temp = -temp
  298.                     opSp = opSp - 1
  299.                 End If
  300.                 AppendToCompiled opValue, temp
  301.                 sp = sp + 1
  302.                 waitForOperator = True
  303.     
  304. #If SupportStrings Then
  305.             Case """", "'"
  306.                 ' a string constant
  307.                 temp = GetString(expr, Index)
  308.                 If m_ErrorCode = expSyntaxError Then GoTo CompileExprSyntaxError
  309.                 AppendToCompiled opValue, temp
  310.                 sp = sp + 1
  311.                 waitForOperator = True
  312. #End If
  313.             Case "+"
  314.                 ' unary plus - it is simply skipped over
  315.                 Index = Index + 1
  316.     
  317.             Case "-"
  318.                 ' unary minus
  319.                 opcode = opMinus
  320.                 GoSub CompileExprPushOpcode
  321.                 Index = Index + 1
  322.     
  323.             Case "("
  324.                 opcode = opOpenBracket
  325.                 GoSub CompileExprPushOpcode
  326.                 Index = Index + 1
  327.     
  328.             Case "A" To "Z", "a" To "z"
  329.                 ' this can be the NOT operator, a function name or a variable name
  330.                 temp = GetName(expr, Index)
  331.                 opcode = FunctionOpcode(UCase$(temp))
  332.                 
  333.                 If opcode = opNot Then
  334.                     GoSub CompileExprPushOpcode
  335.                 ElseIf opcode <> opUnknown Then
  336.                     ' we have found the name of a function
  337.                     If opNumArgs(opcode) = 0 Then
  338.                         ' zero-arg function are very like variables
  339.                         AppendToCompiled opcode
  340.                         sp = sp + 1
  341.                         waitForOperator = True
  342.                         ' zero-arg function may be followed by empty brackets
  343.                         If Mid$(expr, Index, 2) = "()" Then
  344.                             Index = Index + 2
  345.                         End If
  346.                     ElseIf Mid$(expr, Index, 1) = "(" Then
  347.                         ' push the function opcode onto the stack
  348.                         GoSub CompileExprPushOpcode
  349.                         ' push the open bracket onto the stack ...
  350.                         opcode = opOpenBracket
  351.                         GoSub CompileExprPushOpcode
  352.                         Index = Index + 1
  353.                         ' ... but discard the new item added to argStack
  354.                         argSp = argSp - 1
  355.                     Else
  356.                         ' all other functions must be followed by "("
  357.                         m_ErrorDescription = ": missing brackets"
  358.                         GoTo CompileExprSyntaxError
  359.                     End If
  360.                 Else
  361.                     ' it must be the name of a variable
  362.                     Set newVar = Variable(temp, AutoCreateVariables)
  363.                     If newVar Is Nothing Then
  364.                         m_ErrorDescription = temp
  365.                         GoTo CompileExprUnknownFunction
  366.                     End If
  367.                     AppendToCompiled opValue, newVar
  368.                     sp = sp + 1
  369.                     waitForOperator = True
  370.                 End If
  371.                             
  372.             Case Else
  373.                 ' any other character is a syntax error
  374.                 If Mid$(expr, Index, 1) = opNames(opEnd) Then
  375.                     m_ErrorDescription = ": unexpected end of expression"
  376.                 Else
  377.                     m_ErrorDescription = ": unknown symbol"
  378.                 End If
  379.                 GoTo CompileExprSyntaxError
  380.     
  381.             End Select
  382.     
  383.         Else
  384.     
  385.             ' we are expecting an operator
  386.     
  387.             ' remember which character comes before this one
  388.             temp = Mid$(expr$, Index - 1, 1)
  389.             ' search the opcode
  390.             opcode = OperatorOpcode(expr, Index)
  391.             If opcode = opUnknown Then GoTo CompileExprUnknownOperator
  392.             
  393.             If opcode = opAnd Or opcode = opOr Or opcode = opXor Or opcode = opMod Then
  394.                 ' a few binary operators must be preceeded by a space or a ")"
  395.                 ' and must be followed by a space or a "("
  396.                 If InStr(" )", temp) = 0 Then
  397.                     GoTo CompileExprSyntaxError
  398.                 ElseIf InStr(" (", Mid$(expr$, Index, 1)) = 0 Then
  399.                     GoTo CompileExprSyntaxError
  400.                 End If
  401.             End If
  402.     
  403.             ' if it was not a ")" we must prepare to get an operand
  404.             If opcode <> opCloseBracket Then waitForOperator = False
  405.     
  406.             '---------------------------------------------------------------------
  407.             ' this portion of the routine compares the priority of the
  408.             ' operator just parsed with the priority of other operators
  409.             ' pending in opStack(     If opcode = r       = opXor Or opcopt>