Page 4 of 4

Posted: Sat Mar 11, 2006 5:19 am
by Dare2
Threads like this are few and far between!

This is really good and useful/informative in a number of ways.

Thanks to all who are making it so!

Posted: Thu Apr 06, 2006 9:14 pm
by mike74
I took the liberty of modifying Flype's code slightly and adding some of my own to make an expression evaluator that can operate on numbers with up to 6 digits after the decimal and produce a result that is correct up to 6 digits after the decimal. It uses quads for the operations to avoid potential problems with floating point precision and returns a string instead of a possibly imprecise float. I'm sure it can be improved upon speedwise, especially through the use of pointers and ascii codes as in Flype's code, and it shouldn't be too hard to make it more general for a certain number of decimal places. Two last things: Your expression should be enclosed in parentheses and I'm not guaranteeing anything! Here it is:

Code: Select all

Declare.q Expression() 

Global *expr.BYTE 

Procedure Expected(string.s) 
  MessageRequester("Error","Expected : "+string+", Got : "+Chr(*expr\b)) 
  End 
EndProcedure 
Procedure Match(b.b) 
  If *expr\b = b 
    *expr + 1 
  Else 
    Expected(Chr(b)) 
  EndIf 
EndProcedure 
Procedure.q GetNum() 
  Protected Temp.s 
  If (*expr\b < '0') And (*expr\b > '9') 
    Expected("Integer") 
  EndIf 
  While ((*expr\b >= '0' And *expr\b <= '9') Or *expr\b = '.') 
    Temp + Chr(*expr\b) 
    *expr + 1 
  Wend 
  ProcedureReturn ValF(Temp) 
EndProcedure 
Procedure.q Factor() 
  Protected Value.q 
  If (*expr\b >= '0' And *expr\b <= '9') 
    ProcedureReturn GetNum() 
  Else 
    Match('(') 
    Value = Expression() 
    Match(')') 
  EndIf 
  ProcedureReturn Value 
EndProcedure 
Procedure.q Term() 
  Protected Value.q 
  Value = Factor() 
  While (*expr\b = '*' Or *expr\b = '/') 
    If *expr\b = '*' 
      *expr + 1: Value * Factor() 
    Else 
      *expr + 1: factval.q = factor()
      If ((value % factval) * 2 >= factval)
        value = Value / Factval + 1
      Else: Value / Factval: EndIf
    EndIf 
  Wend 
  ProcedureReturn Value 
EndProcedure 
Procedure.q Expression() 
  Protected Value.q 
  If *expr\b = '-' 
    *expr + 1: Value = -Term() 
  Else 
    Value = Term() 
  EndIf 
  While (*expr\b = '+' Or *expr\b = '-') 
    If *expr\b = '+' 
      *expr + 1: Value + Term() 
    Else 
      *expr + 1: Value - Term() 
    EndIf 
  Wend 
  ProcedureReturn Value 
EndProcedure 
Procedure.s Solve(input.s) 
  *expr = @Input
  qstr.s = StrQ(Expression())
  If Len(qstr) >= 6: rtrnstr.s = Left(qstr, (Len(qstr) - 6)) + "." + Right(qstr, 6)
    Else: rtrnstr = ".": For t = 1 To (6 - Len(qstr)): rtrnstr + "0": Next t: rtrnstr + qstr: EndIf
  ProcedureReturn rtrnstr
EndProcedure 

Procedure.s stringeval(in.s)
    lastfound = 1: multcnt = 0: divcnt = 0: lastop.s = ""
    For j = 1 To Len(in)
        founddec = 0: foundnum = 0: fact$ = ""
        getchar.s = Mid(in, j, 1)
        If getchar = "*" Or getchar = "/" Or getchar = "+" Or getchar = "-" Or getchar = "(" Or getchar = ")"
            If getchar = "/": divcnt = 1: EndIf
            If (getchar = "*" And multcnt > 0) Or (getchar = "/" Or getchar = "+" Or getchar = "-" Or getchar = ")")
                For x = 1 To multcnt: fact$ + "*1/1000000": Next x
                If divcnt = 1: fact$ + "*1*1000000": EndIf
                multcnt = 0: divcnt = 0
            Else
                fact$ = "*1"
            EndIf
            If lastop = "/": fact$ = "*1": EndIf
            For y = (j - 1) To 1 Step -1
                k.s = Mid(in, y, 1)
                If Str(Val(k)) <> k And k <> ".": Break: Else
                    foundnum = 1
                    If k = ".": part$ = part$ + Mid(in, lastfound, (j - lastfound)) + Space(7 - (j - y)) + fact$ + getchar
                        lastfound = j + 1: founddec = 1: Break: EndIf
                EndIf
            Next y
            If founddec = 0 And foundnum = 1: part$ = part$ + Mid(in, lastfound, (j - lastfound)) + Space(6) + fact$ + getchar: lastfound = j + 1: EndIf
            lastop = getchar
            If lastop = "*": multcnt = multcnt + 1: EndIf
        EndIf
    Next j
    part$ = RemoveString(ReplaceString(part$, " ", "0"), "0.")
    part$ = RemoveString(part$, ".")
ProcedureReturn part$
EndProcedure
string2eval.s = "(2*0.03/6*0.7)"
Debug stringeval(string2eval)
result.s = solve(stringeval(string2eval))
Debug result

Posted: Fri Apr 07, 2006 2:35 am
by dracflamloc
Every thing is welcome and appreciated!

Re: Need a fast infix math evaluator

Posted: Tue Dec 10, 2013 8:00 pm
by aston
hi to all!
I am looking into Trond evaluator and i can say that is really very fast
and almost the simpliest one we can find on net.
Is anyone maybe add variable name(LookUp) in it?
I have try with procedure GetNum() but it looks that not work.
In my tests i use asc() insted of poke and peek...
anyone ?

Re: Need a fast infix math evaluator

Posted: Tue Dec 10, 2013 8:19 pm
by aston
sorry i forget to post code,here is ...

Code: Select all

OpenConsole()

Global Look.s
Look.s = " " ; Yes, that space is IMPORTANT!!!! Remove and DIE!!!
Global Stream.s
Global StreamPos.l        ; Read position in stream

Procedure.s VisibleToken(s.s)
  Select s.s
    Case #CR$
      s.s = "newline"
    Case #LF$
      s.s = "newline"
    Case ""
      s.s = "nothing"
  EndSelect
  ProcedureReturn s.s
EndProcedure

;Cleanup then end
Procedure Finish()
  PrintN("Ending.")
  Input()
  End
EndProcedure

;Report an error
Procedure Error(s.s)
  PrintN("Error: " + s + ".")
EndProcedure

;Report an error and abort
Procedure Abort(s.s)
  Error(s.s)
  Finish()
EndProcedure

;Report what was expected
Procedure Expected(expected.s)
  Abort("Expected: " + expected + ", got '" + VisibleToken(Look) + "'")
EndProcedure

;Read a character into Look
Goto GetChar_End
GetChar:
  PokeB(@Look, PeekB(@Stream + StreamPos))
  StreamPos + 1
  Return
GetChar_End:

;Match a specific input character
Procedure Match(s.s)
  If Look <> s.s
    Expected("'"+s.s+"'")
  Else
    !CALL l_getchar
  EndIf
EndProcedure

;Get a number
Procedure.f GetNum()
  Protected Temp.s
  If (PeekB(@Look) < 48 And PeekB(@Look) > 57)
    Expected("Integer")
  EndIf
;  While (PeekB(@Look) > 47 And PeekB(@Look) < 58 Or PeekB(@Look) = '.')   ; Works not
  While ((PeekB(@Look) > 47 And PeekB(@Look) < 58) Or PeekB(@Look) = '.') ; Works
    Temp + Look
    !CALL l_getchar
  Wend
  ProcedureReturn ValF(Temp)
EndProcedure

Procedure.f GetVar()
  Protected Temp.s
  If (PeekB(@Look) < 96 And PeekB(@Look) > 123)
    Expected("Variable")
  EndIf
;  While (PeekB(@Look) > 47 And PeekB(@Look) < 58 Or PeekB(@Look) = '.')   ; Works not
  While ((PeekB(@Look) > 96 And PeekB(@Look) < 123) ) ; Works
    Temp + Look
    !CALL l_getchar
  Wend
  MessageRequester("TEMP",Temp)
  ; is this place good to get variable value from external proceduere like GetVarValue()?
  ProcedureReturn ValF(Temp)
EndProcedure


;-----

Declare.f Expression()

Procedure.f Factor()
  Protected Value.f
  If (PeekB(@Look) > 47 And PeekB(@Look) < 58)
    ProcedureReturn GetNum()
  EndIf
  If (PeekB(@Look) > 96 And PeekB(@Look) < 123)
    MessageRequester("","is variable...")
    ProcedureReturn GetVar()
  EndIf
  If look
    Match("(")
    Value = Expression()
    Match(")")
  EndIf
  ProcedureReturn Value
EndProcedure

Procedure.f Term()
  Protected Value.f
  Value = Factor()
  While (Look = "*" Or Look = "/")
    If Look = "*"
      !CALL l_getchar
      Value * Factor()
    Else
      !CALL l_getchar
      Value / Factor()
    EndIf
  Wend
  ProcedureReturn Value
EndProcedure

Procedure.f Expression()
  Protected Value.f
  If (Look = "-")
    !CALL l_getchar
    Value = -Term()
  Else
    Value = Term()
  EndIf
  While (Look = "+" Or Look = "-")
    If Look = "+"
      !CALL l_getchar
      Value + Term()
    Else
      !CALL l_getchar
      Value - Term()
    EndIf
  Wend
  ProcedureReturn Value
EndProcedure

Procedure.f Solve(s.s)
  Stream = ReplaceString(s.s, " ", "")
  StreamPos = 0
  !CALL l_getchar
  Temp.f = Expression()
  If StreamPos < Len(Stream) ; Error check, you
    Expected("nothing")      ; can remove them if you don't
  EndIf                      ; need the check
  ProcedureReturn Temp.f
EndProcedure

#Tries = 100000

time = GetTickCount_()
For I = 0 To #Tries
  Solve("100+1")
Next
MessageRequester("", Str(GetTickCount_()-time))

PrintN(StrF( Solve("(1+2)*abc") ))
;PrintN(StrF( Solve("(1+2)*3") ))
Input()

Re: Need a fast infix math evaluator

Posted: Wed Dec 11, 2013 8:05 am
by Danilo
aston wrote:hi to all!
I am looking into Trond evaluator and i can say that is really very fast
and almost the simpliest one we can find on net.
Is anyone maybe add variable name(LookUp) in it?
I have try with procedure GetNum() but it looks that not work.
In my tests i use asc() insted of poke and peek...
anyone ?
If I understand correctly, you want to use variables within expressions?
aston wrote:sorry i forget to post code,here is ...
Your code did not work here. (EDIT: Sorry, had Unicode enabled by default. Your code works with Unicode disabled)

You could try the following code, if speed is not the most important thing for you:

Code: Select all

DisableDebugger ; for time measurement

XIncludeFile "Evaluate.pbi" ; http://danilo.purearea.net/EvaluateExpression.zip

OpenConsole()

#Tries = 100000

Define i, start_time = ElapsedMilliseconds()

For i = 1 To #Tries
  Evaluate("100+1")
Next

Define end_time = ElapsedMilliseconds()-start_time

;MessageRequester("", Str(end_time)+" ms ("+Str(#Tries)+" tries)" )
PrintN( Str(end_time)+" ms ("+Str(#Tries)+" tries)" )

PrintN( Evaluate("100+1")     )

EvaluateVariables("abc") = "10"
PrintN( Evaluate("(1+2)*abc") )

PrintN( Evaluate("(1+2)*3")   )
Input()
Unfortunately it takes round about 275 milliseconds here to evaluate "100+1" 100.000 times. Don't know if this
is fast enough for you, but at least it works cross-platform in Ascii/Unicode mode, has the variables support
you want, and it even supports some more operators.

Re: Need a fast infix math evaluator

Posted: Wed Dec 11, 2013 1:27 pm
by aston
Hi danilo
Yes i don't need unicode or any other complex stuff.
ok i see your evaluator is on pureArea...thanks!
Speed is not critical and if you say that it takes 275 ms that is very fine for me. :)
Important thing is that there is no any kind of memory leaks
thanks again!

EDIT..
Uff your code is cool but is little bit to complex for me to understand...
Is there any simple evaluator created in more classic way...
thanks!

Re: Need a fast infix math evaluator

Posted: Wed Dec 11, 2013 7:50 pm
by Danilo
aston wrote:EDIT..
Uff your code is cool but is little bit to complex for me to understand...
Is there any simple evaluator created in more classic way...
Your last code with added map SolveVariables() for procedure GetVar():

Code: Select all

OpenConsole()

Global Look.s
Look.s = " " ; Yes, that space is IMPORTANT!!!! Remove and DIE!!!
Global Stream.s
Global StreamPos.l        ; Read position in stream

Procedure.s VisibleToken(s.s)
  Select s.s
    Case #CR$
      s.s = "newline"
    Case #LF$
      s.s = "newline"
    Case ""
      s.s = "nothing"
  EndSelect
  ProcedureReturn s.s
EndProcedure

;Cleanup then end
Procedure Finish()
  PrintN("Ending.")
  Input()
  End
EndProcedure

;Report an error
Procedure Error(s.s)
  PrintN("Error: " + s + ".")
EndProcedure

;Report an error and abort
Procedure Abort(s.s)
  Error(s.s)
  Finish()
EndProcedure

;Report what was expected
Procedure Expected(expected.s)
  Abort("Expected: " + expected + ", got '" + VisibleToken(Look) + "'")
EndProcedure

;Read a character into Look
Goto GetChar_End
GetChar:
  PokeB(@Look, PeekB(@Stream + StreamPos))
  StreamPos + 1
  Return
GetChar_End:

;Match a specific input character
Procedure Match(s.s)
  If Look <> s.s
    Expected("'"+s.s+"'")
  Else
    !CALL l_getchar
  EndIf
EndProcedure

;Get a number
Procedure.f GetNum()
  Protected Temp.s
  If (PeekB(@Look) < 48 And PeekB(@Look) > 57)
    Expected("Integer")
  EndIf
;  While (PeekB(@Look) > 47 And PeekB(@Look) < 58 Or PeekB(@Look) = '.')   ; Works not
  While ((PeekB(@Look) > 47 And PeekB(@Look) < 58) Or PeekB(@Look) = '.') ; Works
    Temp + Look
    !CALL l_getchar
  Wend
  ProcedureReturn ValF(Temp)
EndProcedure

Global NewMap SolveVariables.s() ; ADDED BY DANILO

Procedure.f GetVar()
  Protected Temp.s
  If (PeekB(@Look) < 96 And PeekB(@Look) > 123)
    Expected("Variable")
  EndIf
;  While (PeekB(@Look) > 47 And PeekB(@Look) < 58 Or PeekB(@Look) = '.')   ; Works not
  While ((PeekB(@Look) > 96 And PeekB(@Look) < 123) ) ; Works
    Temp + Look
    !CALL l_getchar
  Wend
  ; MessageRequester("TEMP",Temp)
  ; is this place good to get variable value from external proceduere like GetVarValue()?
  ProcedureReturn ValF(SolveVariables(Temp)) ; CHANGED BY DANILO
EndProcedure


;-----

Declare.f Expression()

Procedure.f Factor()
  Protected Value.f
  If (PeekB(@Look) > 47 And PeekB(@Look) < 58)
    ProcedureReturn GetNum()
  EndIf
  If (PeekB(@Look) > 96 And PeekB(@Look) < 123)
    ;MessageRequester("","is variable...")
    ProcedureReturn GetVar()
  EndIf
  If look
    Match("(")
    Value = Expression()
    Match(")")
  EndIf
  ProcedureReturn Value
EndProcedure

Procedure.f Term()
  Protected Value.f
  Value = Factor()
  While (Look = "*" Or Look = "/")
    If Look = "*"
      !CALL l_getchar
      Value * Factor()
    Else
      !CALL l_getchar
      Value / Factor()
    EndIf
  Wend
  ProcedureReturn Value
EndProcedure

Procedure.f Expression()
  Protected Value.f
  If (Look = "-")
    !CALL l_getchar
    Value = -Term()
  Else
    Value = Term()
  EndIf
  While (Look = "+" Or Look = "-")
    If Look = "+"
      !CALL l_getchar
      Value + Term()
    Else
      !CALL l_getchar
      Value - Term()
    EndIf
  Wend
  ProcedureReturn Value
EndProcedure

Procedure.f Solve(s.s)
  Stream = ReplaceString(s.s, " ", "")
  StreamPos = 0
  !CALL l_getchar
  Temp.f = Expression()
  If StreamPos < Len(Stream) ; Error check, you
    Expected("nothing")      ; can remove them if you don't
  EndIf                      ; need the check
  ProcedureReturn Temp.f
EndProcedure

#Tries = 100000

time = ElapsedMilliseconds()
For I = 0 To #Tries
  Solve("100+1")
Next
MessageRequester("", Str(ElapsedMilliseconds()-time))

SolveVariables("abc") = "1.5"
PrintN(StrF( Solve("(1+2)*abc") ))
PrintN(StrF( Solve("(1+2)*3") ))
PrintN(StrF( Solve("x + y") ))
Input()
This returns 0 for unknown variables: PrintN(StrF( Solve("x + y") ))
Add an error message if the map element TEMP can't be found, to change this behavior:

Code: Select all

Procedure.f GetVar()
  Protected Temp.s
  If (PeekB(@Look) < 96 And PeekB(@Look) > 123)
    Expected("Variable")
  EndIf
;  While (PeekB(@Look) > 47 And PeekB(@Look) < 58 Or PeekB(@Look) = '.')   ; Works not
  While ((PeekB(@Look) > 96 And PeekB(@Look) < 123) ) ; Works
    Temp + Look
    !CALL l_getchar
  Wend
  ; MessageRequester("TEMP",Temp)
  ; is this place good to get variable value from external proceduere like GetVarValue()?
  If FindMapElement(SolveVariables(),Temp)=0
      ; output Error
      Abort( "Variable '"+Temp+"' not initialized." )
  EndIf
  ProcedureReturn ValF(SolveVariables(Temp)) ; CHANGED BY DANILO
EndProcedure

Re: Need a fast infix math evaluator

Posted: Wed Dec 11, 2013 11:02 pm
by aston
Yes that is , it is really the simpliest solution !
Thank you very much Danilo :)

Re: Need a fast infix math evaluator

Posted: Sat Dec 14, 2013 7:23 pm
by aston
hi again...
I really don't bothering you with problems.
But i must ask something ...
I think that some of you guys here( in first place i mean ) on people who have
experience with scripting languages or are authors of them.
Why this type or similar types of expression evaluators produce very
high memory usage when is used inside loops.
if i use anything else inside loop what is not calcualtion then there is no
memory leak...
i hope that my question is not too stupid... :?

Re: Need a fast infix math evaluator

Posted: Mon Dec 16, 2013 8:26 pm
by acreis
Don't worry, your question is not stupid, and there is no reason for memory leaks if your code is correct.

Best Regards

Re: Need a fast infix math evaluator

Posted: Tue Dec 17, 2013 7:56 am
by Danilo
aston wrote:Why this type or similar types of expression evaluators produce very
high memory usage when is used inside loops.
if i use anything else inside loop what is not calcualtion then there is no
memory leak...
i hope that my question is not too stupid... :?
How do you test for memory leaks? Can you give some code?

Sometimes OS memory management allocates more and more fresh
memory blocks on the heap, but it is not a real memory leak, and the memory
is released by the OS later. It is not so easy sometimes, so best you can do is
provide some example code where you see the possible leak, and at the same
time we could see how you measure the memory usage.

For example, on Windows you should call EmptyWorkingSet_() and HeapCompact_()
right before measuring memory usage (for debugging only) when looking for memory leaks,
to prevent the OS behavior I talked about.

With PureBasic itself, you should disable the debugger when looking for memory leaks,
as the debugger could disturb memory usage statistics. The debugger could collect infos
or strings get added to the debug output window (and those strings occupy some memory).

Also PB version, and Operating System and its version are important when looking for such issues,
as there are many possible combinations (Win95 - Win8.1, GNU/Linux X.XX, Mac OS X version XX.XX,
latest PB 5.21 LTS or any older version).

Re: Need a fast infix math evaluator

Posted: Fri Jan 17, 2014 8:54 am
by aston
How do you test for memory leaks?
heh..by looking into task manager....eh...

By the way ...anyone maybe have a idea how to add into this tronD based
parser possibility to detect function like sin(90) ?
thanks in advance :)

Re: Need a fast infix math evaluator

Posted: Tue Jan 21, 2014 5:40 pm
by aston
anyone ?

Re: Need a fast infix math evaluator

Posted: Tue Jan 21, 2014 6:17 pm
by Tenaja
aston wrote:anyone ?
This is taken from mike74's code at the top of page 4. It is not tested to be finished; I leave that to you. I just wanted to point you in the right direction.

Code: Select all

Procedure.q Factor()
	Protected Value.q
	
	If (*expr\b >= '0' And *expr\b <= '9')
		ProcedureReturn GetNum()
	ElseIf *expr\b = 's'
		Match('s')
		Match('(')
		Value = Sin(Expression())
		Match(')')
	Else
		Match('(')
		Value = Expression()
		Match(')')
	EndIf
	ProcedureReturn Value
EndProcedure