math evaluator

BlitzPlus Forums/BlitzPlus Programming/math evaluator

rdodson41(Posted 2004) [#1]
Is there any way, or any code already written that lets you take a string of math expressions, such as "2+6/3" and put it into the function and have it come out as 4?
Thanks in advance


soja(Posted 2004) [#2]
That was one of the sample projects/examples in my old C textbook (which I still have). I could look for the code online when I get home if you want.

On the other hand, it shouldn't be too hard to write using a recursive function.


Curtastic(Posted 2004) [#3]
Here you go. The function is completely self-contained.

It treats everything as float though.

;examples:
Print eval("3/4")
Print eval("2^(3+2)")
Print eval("5>1 And 5<100")
WaitKey
End

;All values are considered float
;The string must be less than 256 characters long
Function eval#(s$)
	
	Local lens

	s=Lower(s)
	s=Replace(s,"and","&")
	s=Replace(s,"xor","@")
	s=Replace(s,"or","|")
	s=Replace(s,"mod","%")
	lens=Len(s)

	Local value=CreateBank((lens+1)*4)
	Local claimed=CreateBank(lens+1)
	Local m$,i,oldi,gotpoint,nest
	Local cmdpri$,cmdsi$;strings, so numbers can be easily inserted
	Local ci,pri

	
	For i=1 To lens
		m=Mid(s,i,1)
		pri=0
		Select m
		Case "&","|","@"
			pri=1
		Case "=","<",">"
			pri=2
		Case "+","-"
			pri=3
		Case "*","/","%"
			pri=4
		Case "^"
			pri=5
		Case "("
			nest=nest+1
		Case ")"
			nest=nest-1
			If nest<0 Then RuntimeError "too many ')'"
		Default
			If Int(m)<>0 Or m="0" Or m="." Then
				;get number into value bank
				oldi=i
				gotpoint=(m=".")
				Repeat
					i=i+1
					m=Mid(s,i,1)
					If m="." Then
						If gotpoint=1 Then RuntimeError "2 decimal points!"
						gotpoint=1
					Else
						If Int(m)=0 And m<>"0" Then Exit
					EndIf
					PokeByte claimed,i,oldi
				Forever
				PokeFloat value,oldi*4,Float(Mid(s,oldi,i-oldi))
				i=i-1
			Else
				If m<>" " Then RuntimeError "what is '"+m+"' ?"
			EndIf
		End Select
		If pri>0 Then
			;insert operators into list by highest priority to lowest
			pri=pri+5*nest
			For ci=1 To Len(cmdpri)
				If pri>Asc(Mid(cmdpri,ci,1)) Then
					Exit
				EndIf
			Next
			cmdpri=Left(cmdpri,ci-1)+Chr(pri)+Mid(cmdpri,ci)
			cmdsi =Left(cmdsi ,ci-1)+Chr(i  )+Mid(cmdsi ,ci)
		EndIf
	Next
	If nest>0 Then RuntimeError "too many '('"

	Local lenc
	lenc=Len(cmdsi)
	Local mi,ii,ii2,add
	Local lv#,rv#,ans#
	For i=1 To lenc
		mi=Asc(Mid(cmdsi,i,1))
		;find values to the right and left of operator
		For add=-1 To +1 Step 2
			ii=mi+add
			;skip over spaces and parens
			Repeat
				m=Mid(s,ii,1)
				If m<>" " And m<>"(" And m<>")" Then Exit
				ii=ii+add
			Forever
			;magical code that makes sure it uses the correct order of the priorities
			Repeat
				ii2=PeekByte(claimed,ii)
				If ii2=0 Then Exit
				ii=ii2
			Forever
			PokeByte claimed,ii,mi
			If add=-1 Then
				lv=PeekFloat(value,ii*4) 
			Else
				rv=PeekFloat(value,ii*4)
			EndIf
		Next
		;do the math and put result in the value bank a the spot of the operator
		Select Mid(s,mi,1)
			Case "+" ans=lv+rv
			Case "-" ans=lv-rv
			Case "*" ans=lv*rv
			Case "/" ans=lv/rv
			Case "=" ans=lv=rv
			Case "<" ans=lv<rv
			Case ">" ans=lv>rv
			Case "&" ans=lv And rv
			Case "|" ans=lv Or rv
			Case "%" ans=lv Mod rv
			Case "^" ans=lv^rv
			Case "@" ans=lv Xor rv
			Default RuntimeError Mid(s,mi,1)
		End Select
		PokeFloat value,mi*4,ans
	Next

	FreeBank value
	FreeBank claimed
	Return ans
End Function



BlitzSupport(Posted 2004) [#4]
There are a couple of examples here:

http://www.blitzbasic.com/codearcs/codearcs.php?cat=9


ShadowTurtle(Posted 2004) [#5]
This is buggy. This is better: http://www.blitzbasic.com/codearcs/codearcs.php?code=669

cu


rdodson41(Posted 2004) [#6]
Yeah, thanks for all the help, but I found out about RPN (reverse polish notation) and its a lot fast and fewer lines of code. But thanks anyway, ill probably keep some of this in mind.