Mark.s wrote:But here a fast math evaluation. Not much commented.
I will update this, if i have free time. Currently supports next operands: < + - * / > and ( )
@Mark.s: thanks for this example code. The first day you posted it I added comments but before I posted I thought I would extend the code a little bit.
It is still simple but has been made easier to modify. I tried to incorporate documentation by way of using enumerations and more descriptive variable names. It has also been corrected, though most of the corrections you have also incorporated into your posting. I also removed some unnecessary bits that did not affect the results. I think it would also benefit by being modified to not use global variables but to act instead on a pointer to a expression string and also a pointer to the index for the same. I'll leave that as an exercise for those interested in doing so.
Here are some facts about my variation:
- - Operators are still limited to being represented by only a single character.
- Operators have a priority list. This makes it possible for operators to be considered "equal" for determining evaluation order.
- It supports the Operators ^, *, /, +, -, <, >, =, !, &, |, ~, (, )
- The four sets are for math, comparisons, Logical , grouping
- All logical operators and comparisons evaluate to 1 when true and 0 when false.
- The Logical operators include equivalents of And, Or, and Xor. Use parenthesis to group as desired.
- There are two Unary Operators, - and !. They can follow each other and can be repeated any number of times. For instance "---3" will equal -3 and "----3" will equal 3. Likewise "!!3" will equal 1 and "!!!3" will equal 0. You can also obtain some additional abilities by using !. Examples include "!(5 > 3)" for 5<=3, "!(5=3)" for 5<>3, etc.
- The Exponent function has been added to the math operators and also makes possible operations such as square roots by using fraction exponents (i.e. "16^0.5" equals 4).
- Numbers have the arbitrary length limit(i.e. 5 digits) removed. They can include as many digits as a float will allow.
- All expressions evaluate using floating point precision. The output can be formatted to suit ones taste by using Mark.s's Type() function or any similar function such as Round() or Int().
Code: Select all
;Math evaluation software by Mark.s
;evaluates expressions with "^*/+-!<>=&|~()"
;modifications by Demivec (Jared)
EnableExplicit
Global mathExpression.s
Global mathExpressionLength
Global mathExpressionIndex
;math operators
#MathOperatorsList$ = "^*/+-!<>=&|~()" ;operators listed in an arbitrary order
;relative priority for each respective operator in #MathOperatorsLists relative priority
#MathOperatorsPriorityValueList$ = "65544733322211" ;lowest priority = 1, a unary operator has highest priority
Enumeration
#OperatorUnmatched = 0 ;indicates operator was not found in #MathOperatorsList$
#OperatorUnary = 0 ;indicates an operator has only one operand
;the next group of operators are in the same order as in #MathOperatorsList$
#OperatorExponent
#OperatorMultiply
#OperatorDivide
#OperatorAdd
#OperatorSubtact
#OperatorLogicalNot ;unary
#OperatorLessThan
#OperatorGreaterThan
#OperatorLogicalEqual
#OperatorLogicalAnd
#OperatorLogicalOr
#OperatorLogicalXor
#OperatorLeftParen
#OperatorRightParen
;the remaining operators signal special conditions
#OperatorNewExpression
#OperatorInitExpressionEval
EndEnumeration
Procedure.f evalMathExpression(previousOperator = #OperatorInitExpressionEval)
Protected currentChar.s, isFirstOperand, operator, x.f, y.f
;uses Global mathExpression, mathExpressionLength, mathExpressionIndex
If previousOperator = #OperatorInitExpressionEval
mathExpressionLength = Len(mathExpression)
mathExpressionIndex = 0
EndIf
isFirstOperand = #True
Repeat
mathExpressionIndex + 1
currentChar.s = Mid(mathExpression, mathExpressionIndex, 1)
;attempt to identify an operator
operator = FindString(#MathOperatorsList$, currentChar, 1)
If operator = #OperatorSubtact And isFirstOperand
;perform a unary negation operation
x = -evalMathExpression(#OperatorUnary)
ElseIf operator = #OperatorLogicalNot And isFirstOperand
;perform a unary Logical Not operation
If evalMathExpression(#OperatorUnary)
x = 0
Else
x = 1
EndIf
ElseIf operator = #OperatorLeftParen
x = evalMathExpression(#OperatorNewExpression)
ElseIf operator
;check if next operator is of lower priority
If Val(Mid(#MathOperatorsPriorityValueList$,operator,1)) <= Val(Mid(#MathOperatorsPriorityValueList$,previousOperator,1))
;rollback index to previous character
mathExpressionIndex - 1
ProcedureReturn x
EndIf
If operator = #OperatorRightParen
ProcedureReturn x
EndIf
;evaluate sub-expression after operator
y = evalMathExpression(operator)
Select operator
Case #OperatorExponent
x = Pow(x,y)
Case #OperatorMultiply
x = x * y
Case #OperatorDivide
x = x / y
Case #OperatorAdd
x = x + y
Case #OperatorSubtact
x = x - y
Case #OperatorLessThan
If x < y
x = 1
Else
x = 0
EndIf
Case #OperatorGreaterThan
If x > y
x = 1
Else
x = 0
EndIf
Case #OperatorLogicalAnd
If x And y
x = 1
Else
x = 0
EndIf
Case #OperatorLogicalOr
If x Or y
x = 1
Else
x = 0
EndIf
Case #OperatorLogicalXor
If x Xor y
x = 1
Else
x = 0
EndIf
Case #OperatorLogicalEqual
If x = y
x = 1
Else
x = 0
EndIf
EndSelect
ElseIf isFirstOperand
;character is part of a new number
x = ValF(Mid(mathExpression, mathExpressionIndex))
EndIf
isFirstOperand = #False
Until mathExpressionIndex >= mathExpressionLength
ProcedureReturn x
EndProcedure
;some example
Define dummyInput.s
OpenConsole()
mathExpression = "2^3*5+(1-3)*5" ;Should be 30
PrintN( mathExpression + " = " + StrF(evalMathExpression()))
mathExpression = "(2+2)/(5*2)+(5*2)/2+2-(1*3/2)" ;Should be 5.9
PrintN( mathExpression + " = " + StrF(evalMathExpression()))
mathExpression = "(5>3)+(5<3)+!(5=3)" ;Should be 2
PrintN( mathExpression + " = " + StrF(evalMathExpression()))
mathExpression = "(5&2)*-6+!(5~3)-(0|-3)*3" ;Should be -8
PrintN( mathExpression + " = " + StrF(evalMathExpression()))
dummyInput.s = Input()
I plan to use this as a jumping off point for evaluating some expressions that are more complex. This simple and powerful example has been very helpful. Thanks again.
@Edit: source and comments edited to change the determining of operators priority. It is now possible for different operators to have equal priority. Previously each operator had its own priority. I wanted to do this before but it seemed things were working without it until Jack gave evidence to the contrary.