· 4 years ago · Dec 29, 2020, 10:48 PM
1Imports System.Text ' // for StringBuilder
2
3Public Class Expression
4
5#Region " --- Constants --- "
6
7 ' // the maximum number of arguments a function is allowed to have
8 Private Const MAX_FUNC_ARGUMENTS = 2
9
10#End Region
11#Region " --- Enums --- "
12
13 ' // token type for processing the expression string on creation
14 Private Enum TokenType
15 Null ' // no token (used as initial 'previous token type')
16 Number ' // numerical constant
17 MathOp ' // mathematical operator
18 ParenL ' // left-parenthesis
19 ParenR ' // right-parenthesis
20 Negative ' // special-case negative function
21 Symbol ' // undetermined symbol (could be a variable or function)
22 Variable ' // variable
23 Func ' // function
24 End Enum
25
26 ' // operator associativity
27 Private Enum Associativity
28 Left
29 Right
30 End Enum
31
32 ' // operator type (for evaluation)
33 Private Enum OperatorType
34 Add
35 Subtract
36 Multiply
37 Divide
38 Power
39 End Enum
40
41 ' // function type (for evaluation)
42 Private Enum FunctionType
43 ' // trig functions
44 sin
45 cos
46 tan
47 asin
48 acos
49 atan
50 sinh
51 cosh
52 tanh
53 ' // integer funcs
54 ceil
55 floor
56 round
57 ' // others
58 abs
59 sign
60 exp
61 log
62 neg
63 End Enum
64
65#End Region
66#Region " --- Structures --- "
67
68 ' // token structs, added to the expression list and operator stack
69 ' // token representing a numerical constant
70 Private Structure TokenNumber
71 Private _value As Double
72
73 Public Sub New(val As Double)
74 _value = val
75 End Sub
76
77 Public ReadOnly Property Value As Double
78 Get
79 Return _value
80 End Get
81 End Property
82
83 Public Overrides Function ToString() As String
84 Return _value.ToString
85 End Function
86 End Structure
87
88 ' // token representing a mathematical operator
89 Private Structure TokenOperator
90 Private _precedence As Integer
91 Private _associativity As Associativity
92 Private _string As String
93 Private _opType As OperatorType
94
95 Public Sub New(chr As String, prec As Integer, assoc As Associativity, oType As OperatorType)
96 _precedence = prec
97 _associativity = assoc
98 _string = chr
99 _opType = oType
100 End Sub
101
102 Public Overrides Function ToString() As String
103 Return _string
104 End Function
105
106 Public ReadOnly Property Precedence As Integer
107 Get
108 Return _precedence
109 End Get
110 End Property
111 Public ReadOnly Property Associativity As Associativity
112 Get
113 Return _associativity
114 End Get
115 End Property
116 Public ReadOnly Property OperatorType As OperatorType
117 Get
118 Return _opType
119 End Get
120 End Property
121 End Structure
122
123 ' // token representing a left parenthesis
124 Private Structure TokenParenL
125 Private _dummy As Integer ' // dummy value so the structure has SOME size
126 End Structure
127
128 ' // token representing a variable
129 Private Structure TokenVariable
130 Private _varName As String
131
132 Public Sub New(varName As String)
133 _varName = varName
134 End Sub
135
136 Public Overrides Function ToString() As String
137 Return _varName
138 End Function
139
140 Public ReadOnly Property VariableName As String
141 Get
142 Return _varName
143 End Get
144 End Property
145 End Structure
146
147 ' // token representing a math function
148 Private Structure TokenFunction
149 Private _string As String
150 Private _numParams As Integer
151 Private _funcType As FunctionType
152
153 Public Sub New(str As String, nParams As Integer, fType As FunctionType)
154 _string = str
155 _numParams = nParams
156 _funcType = fType
157 End Sub
158
159 Public Overrides Function ToString() As String
160 Return _string
161 End Function
162
163 Public ReadOnly Property NumParams As Integer
164 Get
165 Return _numParams
166 End Get
167 End Property
168 Public ReadOnly Property FunctionType As FunctionType
169 Get
170 Return _funcType
171 End Get
172 End Property
173 End Structure
174
175#End Region
176#Region " --- Variables --- "
177
178#Region " Shared Internal Definitions "
179
180 ' // Operator table, contains operator defintions
181 Private Shared opTable As Hashtable = generateOperatorTable()
182 ' // Function table, contains math function definitions
183 Private Shared funcTable As Hashtable = generateMathFuncTable()
184
185 ' // shared paren token, because we only need one to ever exist
186 Private Shared _tokenParenL As New TokenParenL
187
188#End Region
189
190 ' // a list representation of the expression in reverse-polish notation
191 ' // NOTE: List(Of Object) supposedly offers better performance than an ArrayList
192 Private rpnExpression As List(Of Object)
193
194 ' // a hash table containing the names of all variables present in the expression (as keys w/o values)
195 ' // this is used as part of the checks to see if all variables are defined before eval occurs
196 Private usedVarTable As Hashtable
197
198 ' // a hash table that contains variable definitions given for use in evaluating the expression
199 Private exprVarTable As Hashtable
200
201 ' // counter for number of unique variables left undefined in the expression (used in eval checks)
202 Private numVarsUndefined As Integer = 0
203
204 ' // the original string supplied to the expression (for returning via ToString)
205 Private origExpressionStr As String
206
207
208#End Region
209#Region " --- Methods --- "
210
211#Region " Constructor "
212
213 ' // shared internal definition generators
214 ' // operator table
215 Private Shared Function generateOperatorTable() As Hashtable
216 Dim o As New Hashtable
217
218 ' // Add all the operators to the hash table as keys, with operator tokens they match
219 ' // Note that we only really need one instance of each token type, because due to garbage collection,
220 ' // and the fact that this is a static-declared table, these will always remain in memory
221 o.Add("+", New TokenOperator("+", 2, Associativity.Left, OperatorType.Add))
222 o.Add("-", New TokenOperator("-", 2, Associativity.Left, OperatorType.Subtract))
223 o.Add("*", New TokenOperator("*", 3, Associativity.Left, OperatorType.Multiply))
224 o.Add("/", New TokenOperator("/", 3, Associativity.Left, OperatorType.Divide))
225 o.Add("^", New TokenOperator("^", 4, Associativity.Right, OperatorType.Power))
226
227 ' // return the hash table
228 Return o
229 End Function
230
231 ' // mathematic function table
232 Private Shared Function generateMathFuncTable() As Hashtable
233 Dim o As New Hashtable
234
235 ' // add all the functions to the hash table as keys, with function tokens they match
236 o.Add("sin", New TokenFunction("sin", 1, FunctionType.sin))
237 o.Add("cos", New TokenFunction("cos", 1, FunctionType.cos))
238 o.Add("tan", New TokenFunction("tan", 1, FunctionType.tan))
239 o.Add("asin", New TokenFunction("asin", 1, FunctionType.asin))
240 o.Add("acos", New TokenFunction("acos", 1, FunctionType.acos))
241 o.Add("atan", New TokenFunction("atan", 1, FunctionType.atan))
242 o.Add("sinh", New TokenFunction("sinh", 1, FunctionType.sinh))
243 o.Add("cosh", New TokenFunction("cosh", 1, FunctionType.cosh))
244 o.Add("tanh", New TokenFunction("tanh", 1, FunctionType.tanh))
245 o.Add("ceil", New TokenFunction("ceil", 1, FunctionType.ceil))
246 o.Add("floor", New TokenFunction("floor", 1, FunctionType.floor))
247 o.Add("round", New TokenFunction("round", 1, FunctionType.round))
248 o.Add("abs", New TokenFunction("abs", 1, FunctionType.abs))
249 o.Add("sign", New TokenFunction("sign", 1, FunctionType.sign))
250 o.Add("exp", New TokenFunction("exp", 1, FunctionType.exp))
251 o.Add("log", New TokenFunction("log", 1, FunctionType.log))
252 o.Add("neg", New TokenFunction("neg", 1, FunctionType.neg))
253 ' // return the hash table
254 Return o
255 End Function
256
257 ' // Creates a new instance of the class using a given string as the expression
258 ' // NOTE: The expression is CASE-SENSITIVE for variables, but CASE-INSENSITIVE for function names
259 Public Sub New(exprStr As String)
260 Dim opStack As New Stack ' // Operator stack for Shunting-yard algorithm
261 Dim tokenBuffer As New StringBuilder() ' // token buffer for building numeric/symbolic tokens
262 Dim tokenType As TokenType
263 Dim prevTokenType As TokenType = Expression.TokenType.Null
264 Dim i As Integer ' // iterator for the current string position
265 Dim strLen As Integer
266
267 ' // if the argument is null, throw an exception
268 If exprStr Is Nothing Then
269 Throw New ArgumentNullException("Expression string cannot be null.")
270 ElseIf exprStr.Length < 1 Then
271 Throw New ArgumentException("Expression string cannot be of zero length.")
272 End If
273
274 ' // create the output array list
275 rpnExpression = New List(Of Object)
276 ' // create the variable-tracking hash tables
277 usedVarTable = New Hashtable
278 exprVarTable = New Hashtable
279
280 ' // copy the original expression string
281 origExpressionStr = exprStr.Clone
282
283 ' // loop through the expression string, generating and handling tokens
284 i = 0
285 strLen = exprStr.Length
286 Do
287 ' // determine the token type by checking the current character
288 Select Case exprStr(i)
289 Case "a" To "z", "A" To "Z"
290 ' // it's the start of a symbolic token (variable or function)
291 tokenType = Expression.TokenType.Symbol
292
293 Case "0" To "9", "."
294 ' // it's the start of a numeric token
295 tokenType = Expression.TokenType.Number
296
297 Case "+", "*", "/", "^"
298 ' // it's a mathematical operator
299 tokenType = Expression.TokenType.MathOp
300
301 Case "("
302 ' // open parenthesis
303 tokenType = Expression.TokenType.ParenL
304
305 Case ")"
306 ' // close parenthesis
307 tokenType = Expression.TokenType.ParenR
308
309 Case " "
310 ' // whitespace, so pass over the character
311 i += 1
312 Continue Do
313
314 Case "-"
315 ' // special case, could be minus sign or negative modifier
316 ' // determine which it is by peeking at the next character
317
318 ' // if we're at the end of the string, then there's an error
319 If (i = strLen) Then
320 Throw New ArgumentException("Syntax error.")
321 Else
322 ' // determine which operator it is by peeking at the next character
323 Select Case exprStr(i + 1)
324 Case "0" To "9", "."
325 ' // it's the start of a number
326 tokenType = Expression.TokenType.Number
327
328 Case "a" To "z", "A" To "Z", "("
329 ' // it's a symbol or paren-block, so this token is the special case negative modifier
330 tokenType = Expression.TokenType.Negative
331
332 Case Else
333 ' // it's a minus operator
334 tokenType = Expression.TokenType.MathOp
335
336 End Select
337 End If
338
339 Case Else
340 ' // unhandled character, throw an exception
341 Throw New ArgumentException(String.Format("Syntax error at position {0}.", i))
342
343 End Select
344
345 ' // check syntax by making sure this token is allowed to follow the previous token type
346 ' // (This is in a separate select case so code doesn't need to be repeated for special case tokens like the '-' character
347 Select Case tokenType
348 Case Expression.TokenType.Number
349 ' // check syntax by making sure this token is allowed to follow the previous
350 If Not ( _
351 prevTokenType = Expression.TokenType.Null Or _
352 prevTokenType = Expression.TokenType.MathOp Or _
353 prevTokenType = Expression.TokenType.ParenL) Then
354 ' // syntax error
355 Throw New ArgumentException(String.Format("Syntax error at position {0}.", i))
356 End If
357
358 Case Expression.TokenType.MathOp
359 ' // check syntax by making sure this token is allowed to follow the previous
360 If Not ( _
361 prevTokenType = Expression.TokenType.Number Or _
362 prevTokenType = Expression.TokenType.Variable Or _
363 prevTokenType = Expression.TokenType.ParenR) Then
364 ' // syntax error
365 Throw New ArgumentException(String.Format("Syntax error at position {0}.", i))
366 End If
367
368 Case Expression.TokenType.Symbol
369 ' // check syntax by making sure this token is allowed to follow the previous
370 If Not ( _
371 prevTokenType = Expression.TokenType.Null Or _
372 prevTokenType = Expression.TokenType.MathOp Or _
373 prevTokenType = Expression.TokenType.ParenL Or _
374 prevTokenType = Expression.TokenType.Negative) Then
375 ' // syntax error
376 Throw New ArgumentException(String.Format("Syntax error at position {0}.", i))
377 End If
378
379 Case Expression.TokenType.Negative
380 ' // check syntax by making sure this token is allowed to follow the previous
381 If Not ( _
382 prevTokenType = Expression.TokenType.Null Or _
383 prevTokenType = Expression.TokenType.MathOp Or _
384 prevTokenType = Expression.TokenType.ParenL) Then
385 ' // syntax error
386 Throw New ArgumentException(String.Format("Syntax error at position {0}.", i))
387 End If
388
389 Case Expression.TokenType.ParenL
390 ' // check syntax by making sure this token is allowed to follow the previous
391 If Not ( _
392 prevTokenType = Expression.TokenType.Null Or _
393 prevTokenType = Expression.TokenType.MathOp Or _
394 prevTokenType = Expression.TokenType.Func) Then
395 ' // syntax error
396 Throw New ArgumentException(String.Format("Syntax error at position {0}.", i))
397 End If
398
399 Case Expression.TokenType.ParenR
400 ' // check syntax by making sure this token is allowed to follow the previous
401 If Not ( _
402 prevTokenType = Expression.TokenType.Number Or _
403 prevTokenType = Expression.TokenType.Variable) Then
404 ' // syntax error
405 Throw New ArgumentException(String.Format("Syntax error at position {0}.", i))
406 End If
407
408 End Select
409
410 ' // now that the token type has been determined and the syntax is ok, create the appropriate token object
411 Select Case tokenType
412 Case Expression.TokenType.Number
413 Dim decimalFound As Boolean = False
414
415 ' // build the numeric token (clear it first)
416 tokenBuffer.Clear()
417 ' // add the current character
418 tokenBuffer.Append(exprStr(i))
419 i += 1
420
421 ' // add more characters until we hit the end of string or another condition
422 Do While i < strLen
423 ' // handle the next character
424 Select Case exprStr(i)
425 Case "0" To "9"
426 tokenBuffer.Append(exprStr(i))
427
428 Case "."
429 If decimalFound = True Then
430 ' // a decimal is already in this string, so throw an error
431 Throw New ArgumentException(String.Format("Syntax error at position {0}.", i))
432 End If
433 decimalFound = True
434 tokenBuffer.Append(exprStr(i))
435
436 Case " ", "+", "-", "*", "/", "^", ")"
437 ' // operators, close parens, and whitespace break the loop
438 Exit Do
439
440 Case Else
441 ' // anything else is a syntax error
442 Throw New ArgumentException(String.Format("Syntax error at position {0}.", i))
443 End Select
444
445 i += 1
446 Loop
447
448 ' // the numeric token has been built
449 Dim token As New TokenNumber(CDbl(tokenBuffer.ToString))
450
451 ' // add the token to the RPN output
452 rpnExpression.Add(token)
453
454 Case Expression.TokenType.Symbol
455 ' // build the symbolic token (clear it first)
456 tokenBuffer.Clear()
457 ' // add the current character
458 tokenBuffer.Append(exprStr(i))
459 i += 1
460
461 ' // add more characters until we hit the end of string or another condition
462 Do While i < strLen
463 ' // handle the next character
464 Select Case exprStr(i)
465 Case "a" To "z", "A" To "Z", "0" To "9"
466 tokenBuffer.Append(exprStr(i))
467
468 Case " ", "+", "-", "*", "/", "^", ")", "("
469 ' // whitespace, operatoes, and both parens safely break the loop
470 Exit Do
471
472 Case Else
473 ' // anything else is a syntax error
474 Throw New ArgumentException(String.Format("Syntax error at position {0}.", i))
475 End Select
476
477 i += 1
478 Loop
479
480 ' // check if the (lower case) token exists in the function table as a key
481 If funcTable.ContainsKey(tokenBuffer.ToString.ToLower) Then
482 ' // it's a valid function, push the token onto the stack
483 opStack.Push(funcTable(tokenBuffer.ToString.ToLower))
484 tokenType = Expression.TokenType.Func
485
486 Else
487 ' // it's a variable, make a new token and add it to the output
488 rpnExpression.Add(New TokenVariable(tokenBuffer.ToString))
489 tokenType = Expression.TokenType.Variable
490
491 ' // add the variable to the list of vars found in the expression, if it's not present yet
492 If usedVarTable.ContainsKey(tokenBuffer.ToString) = False Then
493 usedVarTable.Add(tokenBuffer.ToString, Nothing)
494 ' // it's a unique variable name, so increment the undefined var counter
495 numVarsUndefined += 1
496 End If
497
498 ' // If there is a NEG function atop the operator stack, pop it into the output queue, too
499 If opStack.Count > 0 AndAlso opStack.Peek.GetType = GetType(TokenFunction) AndAlso opStack.Peek.FunctionType = FunctionType.neg Then
500 rpnExpression.Add(opStack.Pop)
501 End If
502 End If
503
504 Case Expression.TokenType.Negative
505 ' // advance past the token character
506 i += 1
507 ' // push a negative function onto the stack
508 opStack.Push(funcTable("neg"))
509
510 Case Expression.TokenType.MathOp
511 ' // get the operator token from the operator table
512 Dim op1 As TokenOperator = opTable.Item(exprStr(i).ToString)
513 Dim op2 As TokenOperator
514
515 ' // handle the new operator based on the shunting-yard algorithm rules
516 ' // if the top item on the operator stack is an operator (and not a paren)...
517 Do While opStack.Count > 0 AndAlso opStack.Peek.GetType() = GetType(TokenOperator)
518 ' // reference the top of the stack
519 op2 = opStack.Peek
520 ' // if o1 is left-associative and its precedence is <= to o2
521 ' // OR o1 is right-associative and its presedence is < o2
522 If (op1.Associativity = Associativity.Left And op1.Precedence <= op2.Precedence) Or _
523 (op1.Associativity = Associativity.Right And op1.Precedence < op2.Precedence) Then
524 ' // pop o2 off the stack and into the output queue
525 rpnExpression.Add(opStack.Pop())
526 Else
527 ' // break out of this loop
528 Exit Do
529 End If
530 Loop
531 ' // push operator 1 onto the operator stack
532 opStack.Push(op1)
533 ' // advance past the token character
534 i += 1
535
536 Case Expression.TokenType.ParenL
537 ' // push a left paren token onto the stack (we can use the shared one)
538 opStack.Push(_tokenParenL)
539 ' // advance past the token character
540 i += 1
541
542 Case Expression.TokenType.ParenR
543 ' // until the token on the top of the stack is a left parenthesis, pop operators off the stack
544 ' // and into the output queue
545 Do While opStack.Count > 0 AndAlso opStack.Peek.GetType() <> GetType(TokenParenL)
546 rpnExpression.Add(opStack.Pop())
547 Loop
548
549 ' // if the stack is now empty there are mismatched parentheses
550 If opStack.Count = 0 Then
551 Throw New ArgumentException(String.Format("Syntax error at position {0}: mismatched parentheses", i))
552 End If
553 ' // pop the left-paren off and discard it
554 opStack.Pop()
555 ' // If the token at the top of the stack is a function token, pop it onto the output queue
556 If opStack.Count > 0 AndAlso opStack.Peek.GetType() = GetType(TokenFunction) Then
557 rpnExpression.Add(opStack.Pop())
558 ' // If there is a NEG function after that, pop it into the output queue, too
559 If opStack.Count > 0 AndAlso opStack.Peek.GetType = GetType(TokenFunction) AndAlso opStack.Peek.FunctionType = FunctionType.neg Then
560 rpnExpression.Add(opStack.Pop)
561 End If
562 End If
563
564 ' // advance past the token character
565 i += 1
566
567 End Select
568
569 ' // remember this token type for the next one's syntax checking
570 prevTokenType = tokenType
571
572 Loop While i < strLen ' // tokenizing loop
573
574 ' // we are out of tokens to process, so clear the operator stack into the queue
575 Do While opStack.Count > 0
576 If opStack.Peek.GetType = GetType(TokenParenL) Then
577 ' // mismatched parenthesis
578 Throw New ArgumentException(String.Format("Syntax error at position {0}: mismatched parentheses", i))
579 End If
580 rpnExpression.Add(opStack.Pop())
581 Loop
582
583 End Sub
584
585#End Region
586#Region " Variable-Related "
587
588 ' // defines (or redefines) a variable in the expression
589 Public Sub DefineVariable(varName As String, value As Double)
590 ' // argument check the variable name string
591 If varName Is Nothing Then
592 Throw New ArgumentNullException("Variable name string cannot be null.")
593 ElseIf varName.Length < 1 Then
594 Throw New ArgumentException("Variable name string cannot be of zero length.")
595 End If
596
597 ' // add the variable definition if it doesn't exist
598 If exprVarTable.ContainsKey(varName) Then
599 ' // it already exists, so update the value instead
600 exprVarTable(varName) = value
601 Else
602 ' // it doesn't contain it, so add the variable to the table
603 exprVarTable.Add(varName, value)
604 ' // if the variable is also one of the one's actually used in the expression, decrement the unused counter
605 If usedVarTable.ContainsKey(varName) Then
606 numVarsUndefined -= 1
607 End If
608 End If
609 End Sub
610 ' // undefines a variable in the expression
611 Public Sub UndefineVariable(varName As String)
612 ' // argument check the variable name string
613 If varName Is Nothing Then
614 Throw New ArgumentNullException("Variable name string cannot be null.")
615 ElseIf varName.Length < 1 Then
616 Throw New ArgumentException("Variable name string cannot be of zero length.")
617 End If
618
619 ' // throw an exception if the variable isn't already defined
620 If exprVarTable.ContainsKey(varName) = False Then
621 Throw New ArgumentException("Variable doesn't exist.")
622 End If
623
624 exprVarTable.Remove(varName)
625 ' // if it's present in the used var table, increment the unused counter
626 If usedVarTable.ContainsKey(varName) Then
627 numVarsUndefined += 1
628 End If
629 End Sub
630
631#End Region
632#Region " Evaluation "
633
634 ' // evaluates the expression and returns the value
635 Public Function Evaluate() As Double
636 Dim evalStack As New Stack
637
638 ' // throw an exception if there are undefined variables present
639 If numVarsUndefined <> 0 Then
640 Throw New InvalidOperationException("Cannot evaluate expression containing undefined variables.")
641 End If
642
643 ' // iterate through all the tokens in the RPN expression and evaluate them
644 For Each token In rpnExpression
645 ' // handle based on token type
646 Select Case token.GetType
647 Case GetType(TokenNumber)
648 ' // push the value onto the eval stack
649 evalStack.Push(CType(token, TokenNumber).Value)
650
651 Case GetType(TokenVariable)
652 ' // get the variable's value and push it onto the eval stack
653 evalStack.Push(exprVarTable(CType(token, TokenVariable).VariableName))
654
655 Case GetType(TokenOperator)
656 ' // NOTE: all operators are currently assumed to be binary
657 Dim leftVal, rightVal, result As Double
658
659 ' // pop the two values off the stack
660 rightVal = evalStack.Pop
661 leftVal = evalStack.Pop
662
663 ' // branch on operator type
664 Select Case CType(token, TokenOperator).OperatorType
665 Case OperatorType.Add
666 result = leftVal + rightVal
667 Case OperatorType.Subtract
668 result = leftVal - rightVal
669 Case OperatorType.Multiply
670 result = leftVal * rightVal
671 Case OperatorType.Divide
672 ' // TODO: Handle divide by zero cases
673 result = leftVal / rightVal
674 Case OperatorType.Power
675 result = leftVal ^ rightVal
676 End Select
677
678 ' // push the result back on the stack
679 evalStack.Push(result)
680
681 Case GetType(TokenFunction)
682 ' // NOTE: functions are currently assumed to have a single argument
683 Dim args(MAX_FUNC_ARGUMENTS) As Double
684 Dim result As Double
685
686 ' // pop the appropriate number of arguments off the stack
687 ' // NOTE: currently assumed to be 1
688 args(0) = evalStack.Pop
689
690 ' // branch on function type
691 Select Case CType(token, TokenFunction).FunctionType
692 Case FunctionType.abs
693 result = Math.Abs(args(0))
694 Case FunctionType.acos
695 result = Math.Acos(args(0))
696 Case FunctionType.asin
697 result = Math.Asin(args(0))
698 Case FunctionType.atan
699 result = Math.Atan(args(0))
700 Case FunctionType.ceil
701 result = Math.Ceiling(args(0))
702 Case FunctionType.cos
703 result = Math.Cos(args(0))
704 Case FunctionType.cosh
705 result = Math.Cosh(args(0))
706 Case FunctionType.exp
707 result = Math.Exp(args(0))
708 Case FunctionType.floor
709 result = Math.Floor(args(0))
710 Case FunctionType.log
711 result = Math.Log(args(0))
712 Case FunctionType.neg
713 result = -args(0)
714 Case FunctionType.round
715 result = Math.Round(args(0))
716 Case FunctionType.sign
717 result = Math.Sign(args(0))
718 Case FunctionType.sin
719 result = Math.Sin(args(0))
720 Case FunctionType.sinh
721 result = Math.Sinh(args(0))
722 Case FunctionType.tan
723 result = Math.Tan(args(0))
724 Case FunctionType.tanh
725 result = Math.Tanh(args(0))
726 End Select
727
728 ' // push the result back onto the stack
729 evalStack.Push(result)
730
731 End Select ' // end token type select
732
733 Next ' // end token loop
734
735 Return evalStack.Pop
736 End Function
737
738#End Region
739#Region " String Conversion "
740
741 ' // returns the expression as a string in its reverse-polish notation form
742 Public Function ToStringRPN() As String
743 Dim rpnString As New StringBuilder
744
745 For Each o As Object In rpnExpression
746 rpnString.Append(o.ToString)
747 rpnString.Append(" ")
748 Next
749
750 Return rpnString.ToString
751 End Function
752
753 ' // returns the original string used to build the expression
754 Public Overrides Function ToString() As String
755 Return origExpressionStr
756 End Function
757
758#End Region
759
760#End Region
761
762End Class
763