Code archives/Algorithms/Evaluate String Arithmetic

This code has been declared by its author to be Public Domain code.

Download source code

Evaluate String Arithmetic by daaan2006
UPDATE:
I updated this entry on August 17th 2006. The evaluator was re-written and runs a lot faster now. The new syntax is as follows:

Evaluate:Float( Line:String )

You would use it as follows.

Local Value:Float = Evaluate( "5+3/(2*2)-12/4*(4*(3*3))" )
' string evaluator by Daniel Wooden
' email questions to dan.wooden@gmail.com

Const STACKSIZE = 25
Global SplitStack:String[ STACKSIZE ]

Function Evaluate:Float( Line:String )
	
	Local PointA:Int = 0
	Local PointB:Int = 0
	
	Local EditLine:String = Line.Replace( " ", "" )
	Local CurrentChr:String = ""
	Local Scope:String = ""
	Local ScopeToReplace:String = ""
	
	While EditLine.Contains( "(" )
		
		For i = 0 To EditLine.Length
			CurrentChr = Mid( EditLine, i, 1 )
			If CurrentChr = "(" Then
				PointA = i
			ElseIf CurrentChr = ")" Then
				PointB = i
			End If
			If PointB <> 0 Then
				Scope = Mid( EditLine, PointA+1, PointB-PointA-1 )
				ScopeToReplace = Mid( EditLine, PointA, PointB-PointA+1 )
				EditLine = EditLine.Replace( ScopeToReplace, CalculateScope( Scope ) )
				PointA = 0
				PointB = 0
			End If
		Next
		
	Wend
	
	Return CalculateScope( EditLine ).ToFloat()
	
End Function

Function CalculateScope:String( Line:String )
	
	Local Index:Int = 0
	Local CurrentChr:String = ""
	Local CurrentLine:String = ""
	
	For i = 0 To Line.Length
		CurrentChr = Mid( Line, i, 1 )
		If CurrentChr <> "*" And CurrentChr <> "/" And CurrentChr <> "+" And CurrentChr <> "-" Then
			CurrentLine :+ CurrentChr
		Else
			SplitStack[ Index ] = CurrentLine
			CurrentLine = ""
			Index :+ 1
			SplitStack[ Index ] = CurrentChr
			Index :+ 1
		End If
	Next
	SplitStack[ Index ] = CurrentLine
	
	#DoItAgain
	For i = 0 To STACKSIZE-1
		
		If SplitStack[ i ] = "*" Or SplitStack[ i ] = "/" Then
			Select SplitStack[ i ]
				Case "*"
					SplitStack[ i-1 ] = SplitStack[ i-1 ].ToFloat() * SplitStack[ i+1 ].ToFloat()
					SplitStack[ i ] = ""
					SplitStack[ i+1 ] = ""
				Case "/"
					SplitStack[ i-1 ] = SplitStack[ i-1 ].ToFloat() / SplitStack[ i+1 ].ToFloat()
					SplitStack[ i ] = ""
					SplitStack[ i+1 ] = ""
			End Select
			
			For j = i To STACKSIZE-3
				SplitStack[ j ] = SplitStack[ j+2 ]
			Next
			
		End If
		
	Next
	
	' Lame hack... I'll fix the real problem later.
	For i = 0 To STACKSIZE-1
		If SplitStack[ i ] = "*" Or SplitStack[ i ] = "/" Then
			Goto DoItAgain
		End If
	Next
	
	While SplitStack[ 1 ] <> ""
		For i = 0 To STACKSIZE-1
			
			If SplitStack[ i ] = "+" Or SplitStack[ i ] = "-" Then
				Select SplitStack[ i ]
					Case "+"
						SplitStack[ i-1 ] = SplitStack[ i-1 ].ToFloat() + SplitStack[ i+1 ].ToFloat()
						SplitStack[ i ] = ""
						SplitStack[ i+1 ] = ""
					Case "-"
						SplitStack[ i-1 ] = SplitStack[ i-1 ].ToFloat() - SplitStack[ i+1 ].ToFloat()
						SplitStack[ i ] = ""
						SplitStack[ i+1 ] = ""
				End Select
				
				For j = i To STACKSIZE-3
					SplitStack[ j ] = SplitStack[ j+2 ]
				Next
				
			End If
			
		Next
		
	Wend
	
	Return SplitStack[ 0 ]
	
End Function

Comments

fredborg2006
Cool, I've added some simple variable stuff :)

Edit: Fixed a small bug


daaan2006
@fredborg - I updated the evaluator so you might want to update your nifty variable stuff.


Bobysait2006
I'm not sure to understand, but... It calculate equations in string ?

maybe we could extend it to a virtual debugger ? like a Console ( for exemple, debugging appli in realtime => setting coord for entitys etc... directly by launching commands to the console, like we could use cheat in games using a console cf half life etc... )


ziggy2007
This is the Superstrict version:



ziggy2007
This is the Superstrict version:
' calculate.string.bmx by Daniel Wooden
' email: <a href=\"mailto:dan.wooden@gmail.com\">dan.wooden@...;
'
' Simple variable stuff added by fredborg
' Keep variable names to single letters, as "x" may be confused with ie. "xx" by the evaluator
' Use FlushVariables() to reset variables.
'
SuperStrict
Const STACKSIZE:Int = 25

Global VarIndex:Int
Global VarStack:String[ STACKSIZE, 2]
Global TermsStack:String[ STACKSIZE ]
Global MulDivStack:String[ STACKSIZE ]
Global AddSubStack:String[ STACKSIZE ]

'
' Example
SetVariable("x = 10")
SetVariable("y = x*x*2")
SetVariable("z = (x+y)/2")
SetVariable("x = x+1")
Print CalculateString("x+y+z")
Print "GetVariable(~qx~q) = "+GetVariable("x")
Local Result:Int
Local M:Int = MilliSecs()
For Local i:Int = 0 To 100
	CalculateString("x+y+z")
Next
result = MilliSecs() - m
Print "Time took to perform 100 calculations:" + result
'
' Verify result
Local x:Float
Local y:Float
Local z:Float
x = 10
y = x*x*2
z = (x+y)/2
x = x+1
Print x+y+z


Function SetVariable( in:String )
	in = in.trim()
	
	Local va:String = String(in[..in.Find("=")]).Trim()
	Local pa:String = in[in.Find("=")+1..]
	'
	' Test if variable already exists
	For Local i:Int = 0 Until VarIndex
		If VarStack[i,0] = va
			VarStack[i,1] = CalculateString( pa )
			Return
		EndIf
	Next
	
	'
	' It's a new variable
	VarStack[VarIndex,0] = va
	VarStack[VarIndex,1] = CalculateString( pa )
	VarIndex :+ 1
	
End Function

Function GetVariable:Float( in:String )
	For Local i:Int = 0 Until VarIndex
		If VarStack[i,0] = in Return Float(VarStack[i,1])
	Next
EndFunction

Function FlushVariables()
	VarIndex = 0
End Function

Function InsertVariables:String( in:String )
	For Local i:Int = 0 Until VarIndex
		in = in.Replace(VarStack[i,0],String(VarStack[i,1]))
	Next
	Return in
End Function

Function CalculateString:Float( Line:String )

	Line = InsertVariables(Line)
	
	Local AnswerLine:String = ""
	Local CurrentChr:String = ""
	
	' Remove Spaces
	For Local i:Int = 0 To Line.Length
		CurrentChr = Chr( Line[ i ] )
		If CurrentChr <> " " Then
			AnswerLine :+ CurrentChr
		End If
	Next

	Local PointA:Int = -1
	Local PointB:Int = -1
	
	While AnswerLine.Contains( "(" )

		For Local i:Int = 0 To AnswerLine.Length
			
			CurrentChr = Chr( AnswerLine[ i ] )
			
			If CurrentChr = "(" Then
				PointA = i
			ElseIf CurrentChr = ")" Then
				PointB = i
			End If
			
			If PointA => 0 And PointB => 0 Then
				
				Local A:String = Mid( AnswerLine, PointA+2, PointB-PointA-1 )
				A = CalculateScope( A )
				Local B:String = Mid( AnswerLine, PointA+1, PointB-PointA+1 )
				
				AnswerLine = AnswerLine.Replace( B, A )
				
				Exit
				
			End If
			
		Next
		
		'#NextCalcStep
		
		PointA = -1
		PointB = -1
		
	Wend
	
	AnswerLine = CalculateScope( AnswerLine )
	
	Return AnswerLine.ToFloat()
	
End Function

Function CalculateScope:Float( Line:String )
	
	SplitTerms( Line )
	CalcTerms()
	Local CalcValue:Float = 0.0
	
	For Local i:Int = 0 To STACKSIZE-1
		If TermsStack[ i ] <> "" Then
			If i = 0 Then
				CalcValue = TermsStack[ i ].ToFloat()
			ElseIf TermsStack[ i ] = "+" Then
				CalcValue :+ TermsStack[ i+1 ].ToFloat()
			ElseIf TermsStack[ i ] = "-" Then
				CalcValue :- TermsStack[ i+1 ].ToFloat()
			End If
		End If
	Next
	
	For Local i:Int = 0 To STACKSIZE-1
		TermsStack[ i ] = ""
		MulDivStack[ i ] = ""
		AddSubStack[ i ] = ""
	Next
	
	Return CalcValue

End Function

Function SplitTerms( Line:String )
	
	Local StackIndex:Int = 0
	Local Value:String = ""
	Local CurrentChr:String = ""
	For Local i:Int = 0 To Line.Length
		CurrentChr = Chr( Line[ i ] )
		If CurrentChr = "+" Or CurrentChr = "-" Then
			TermsStack[ StackIndex ] = Value
			StackIndex :+ 1
			TermsStack[ StackIndex ] = Chr( Line[ i ] )
			StackIndex :+ 1
			Value = ""
		ElseIf i = Line.Length
			TermsStack[ StackIndex ] = Value
		Else
			Value :+ Chr( Line[ i ] )
		End If
	Next
	
End Function

Function CalcTerms()
	
	For Local i:Int = 0 To STACKSIZE-1
		If TermsStack[ i ] <> "+" And TermsStack[ i ] <> "-" And TermsStack[ i ] <> "" Then
			TermsStack[ i ] = MulDiv( TermsStack[ i ] )
		End If
	Next
	
End Function

Function MulDiv:Float( Line:String )
	
	Local Value:String = ""
	Local CurrentChr:String = ""
	Local CalcValue:Float = 0.0
	Local StackIndex:Int = 0
	
	For Local i:Int = 0 To Line.Length
		CurrentChr = Chr( Line[ i ] )
		If CurrentChr = "*" Or CurrentChr = "/"
			MulDivStack[ StackIndex ] = Value
			Value = ""
			StackIndex :+ 1
			MulDivStack[ StackIndex ] = CurrentChr
			StackIndex :+ 1
		ElseIf i = Line.Length
			Value :+ CurrentChr
			MulDivStack[ StackIndex ] = Value
		Else
			Value :+ CurrentChr
		End If
	Next
	
	For Local i:Int = 0 To STACKSIZE-1
		If MulDivStack[ i ] <> "" Then
			If i = 0 Then
				CalcValue = MulDivStack[ i ].ToFloat()
			ElseIf MulDivStack[ i ] = "*" Then
				CalcValue :* MulDivStack[ i+1 ].ToFloat()
			ElseIf MulDivStack[ i ] = "/" Then
				CalcValue :/ MulDivStack[ i+1 ].ToFloat()
			End If
		End If
	Next
	
	Return CalcValue

End Function



daaan2016
Soooo cool to me that people added to this. A nice stroll down bmax-lane.


Code Archives Forum