PDA

View Full Version : General Purpose Eval

JosephE
14-08-2010, 01:12
I need a general purpose eval function that can solve an expression like this:

Val("1") + 0 Or Int(1.5)

That would produce the result TRUE (1 or -1 in thinBASIC).

Notice that the expression has a string, math, and the logical/bitwise operator Or in it. I don't want to have to use Eval_String, or Eval_Math, or Eval() to solve different expressions because I need an evaluator that spits out a variant.

Do I have any options? I have no idea how to write an expression evaluator, I don't think I'm quite that skilled yet.

ErosOlmi
14-08-2010, 01:25
Sorry, I do not understand why Eval_Math cannot be used. Maybe if I underatand I can help.
What does it means:
spits out a variant

Anyhow, here example using Eval module:

Uses "Eval"

MsgBox 0, Eval_Math("Val(""1"") + 0 Or Int(1.5)")

Ciao
Eros

JosephE
14-08-2010, 03:10
I didn't make it very clear. Let me try again. :)

In my code, I'm unaware of what the expression could evaluate to. It's whatever the user made it. What if it was this?

Str\$(Val("1") + 0 Or Int(1.5)) & " hi"

Eval_Math won't return what you would expect:

Uses "Eval"

MsgBox 0, Eval_Math("Str\$(Val(""1"") + 0 Or Int(1.5)) & "" hi""")

It returns "1" where I need it to correctly return "1 hi". That's understandable, because it as a Math evaluator. I need something that returns a variant, like I mentioned.

Thanks for the quick response! :)

ErosOlmi
14-08-2010, 09:42
I do not think it can be a VARIANT the solution because at some point you have to assign the result of your expression to the VARIANT and at that point you need to know what is the main expression main type.

What about something like the following?
All expressions are valuated into a string.
To understand if expression returns a string or a number it checks that FORMAT\$ of the VAL of the return string value must be equal to the original returned string. In this case it should/can be a number.

Uses "Console"
Uses "Eval"

Dim sResult As String
Dim eResult As Ext

PrintL "--- Example 1: -------------------------------------------------"
sResult = Trim\$(Eval_String("Val(""1"") + 0 Or Int(1.5)"))
eResult = Val(sResult)
PrintL "String Result:", sResult
PrintL "Numeric Result:", eResult

If CheckIfEqual(sResult, eResult) Then
PrintL "They seems equal so expression seems numeric"
Else
PrintL "They seems different so expression seems a string"
End If

PrintL
PrintL
PrintL "--- Example 2: -------------------------------------------------"
sResult = Trim\$(Eval_String("Str\$(Val(""1"") + 0 Or Int(1.5)) & "" hi"""))
eResult = Val(sResult)
PrintL "String Result:", sResult
PrintL "Numeric Result:", eResult
If CheckIfEqual(sResult, eResult) Then
PrintL "They seems equal so expression seems numeric"
Else
PrintL "They seems different so expression seems a string"
End If

WaitKey

'---------------------------------------------------------------------------
Function CheckIfEqual(ByVal sBuffer As String, ByVal eNumber As Ext) As Long
'---------------------------------------------------------------------------
If Format\$(eResult) = sResult Then
Function = %TRUE
Else
Function = %FALSE
End If

End Function

ErosOlmi
14-08-2010, 14:11
Joseph,

I've done few changes to Eval module adding the following two functions:

Eval_Variable_Exists(VariableName)
Will check if a variable exists after an Eval... execution
Eval_Reset
Will reset internal Eval structure to its base state (all defined variables during a previous Eval... execution will be removed)

Attached to this post you will find new preliminary version of Eval module. Just download it and substitute the one you will find into \thinBasic\Lib\ directory

Here an example on how it can be possible to solve your problem (just an idea).
Ask to your users to define one variable name (string for example) or another (numeric for example) and than use the presence of one of the two variables to determine what is the correct value to retrieve (string or numeric).

Hope this can help you otherwise let me know your perplexities and I will try to see if I can improve again the module.
Eros

Uses "Console"
Uses "Eval"

Dim nScripts As Long Value 2
Dim sScripts(nScripts) As String
Dim Counter As Long
Dim lReturnCode As Long

'---Simulate user script using a string
sScripts(1) = RawText
Dim Exit_String As String
Exit_String = Str\$(Val("1") + 0 Or Int(1.5)) & " hi"
End RawText

'---Simulate user script using a number
sScripts(2) = RawText
Dim Exit_Number As Number
Exit_Number = Val("1") + 0 Or Int(1.5)
End RawText

'---Execute all the scripts and check existence of one variable or the other
For Counter = 1 To nScripts

lReturnCode = Eval(sScripts(Counter))

'---If execution was fine, check variable names
If lReturnCode = 0 Then
If Eval_Variable_Exists("Exit_String") Then
PrintL "Exit_String is present and its value is:", Eval_GetString("Exit_String")
ElseIf Eval_Variable_Exists("Exit_Number") Then
PrintL "Exit_Number is present and its value is:", Eval_GetNumber("Exit_Number")
Else
PrintL "No usable variables defined"
End If
Else
PrintL "An error occurred during script number " & Counter
Exit For
End If

'---Important: reset eval module between each execution
'---In order to remove all variables defined by previous run
'---If Eval_Reset function will not be executed, all variables
'---defined by a previous Eval will be still there
PrintL
PrintL "--------------------------------------------------------"
PrintL "- Resetting Eval ----------------------------------------"
PrintL "--------------------------------------------------------"
Eval_Reset
Next

WaitKey

JosephE
14-08-2010, 21:33
Eros, you're awesome! :D

While my user does define what expression gets evaluated, it's rather indirect. It's a part of my simple interpreter for a bigger project I've been working on in thinBASIC. thinBASIC and thinAir are holding up really well and I must say it's a pleasure developing with these tools.

I'll only be evaluating literal expressions, no variables are needed, so your first solution works brilliantly for me. My primitive littler interpreter replaces all the user's variables in the current scope with their appropriate values, so your method of detecting the return type is perfect for me.

Those new Eval functions might come in handy one of these days though. Thanks! :)

JosephE
15-08-2010, 22:28
How come Eval_String() puts a blank space in front of some of the results?

Uses "Console"
Uses "Eval"

PrintL Evaluate("""hello, "" & ""world!""")
PrintL Evaluate("(0 Or 0) = (1 and 0)")
PrintL Evaluate("Str\$(1 And Val(""-1"")) & "".""")
PrintL Evaluate("""bob"" = ""bob"" And ""willy"" = ""willy""")
PrintL Evaluate("1 = 1")
PrintL Evaluate("Val(""9000000"" & ""0"") + (15 + sin(11.211) / 2 & sqr(7))")
WaitKey

Function Evaluate(strng As String) As String
Return Eval_String(strng)
End Function

ErosOlmi
15-08-2010, 22:46
Attached a fixed version of Eval module.
Just download it and substitute the one you will find into \thinBasic\Lib\ directory

The example using STR\$(...) will still retunr a space in front of the returned buffer because in almost all BASIC languages STR\$ add a space in front of the converted number (I think for hystorical reasons it leaves a space for sign in case of negative numbers)

Eros

JosephE
15-08-2010, 23:06
Thanks Eros, that's wonderful. I didn't know Str\$() produces a space, so that makes everything work out just dandy. :)

ErosOlmi
15-08-2010, 23:08
If you prefer not to have space do not use STR\$(number) but use FORMAT\$(number)

Ciao
Eros

JosephE
16-08-2010, 22:27
Thanks Eros, I wasn't too familiar with the Format\$() function.

Why does this return 1? Eval_Math() returns 0 correctly. In my implementation, I'm blind as to what we're evaluating. So how do I tell which evaluator is right?

Your code checks to see if the number from string and the string are the same thing. If they are, it assumes it's a number. But in this case, it's different (for some reason unknown to me), so it's throwing it off. :roll:

Uses "Console"
Uses "Eval"

PrintL Evaluate("1 = 0")

WaitKey

Function Evaluate(strng As String) As String
Return Eval_String(strng)
End Function

ErosOlmi
17-08-2010, 06:12
Mainly because all logical comparisons are numerical expressions and not string expressions.

"1 = 0"
is a numerical expression and has no meaning for a string expression.

STR\$(1 = 0)
is a string expression and here 1 = 0 will correctly be evaluated as 0 because STR\$() expect a numerical expression.

Of course I can improve Eval module but I would go far behind its purpose and creating a full interpreter inside an interpreter would require too much time.
In any case if necessary I can think about it.

JosephE
17-08-2010, 23:24
Don't worry about it for now, I'll survive without it for the time being. Although I probably will need this when development gets further along.

JosephE
21-08-2010, 08:59
This here produces the correct result on string, number, and comparison results:

I'm happy. :)

Uses "Console"
Uses "Eval"

PrintL Evaluate("Str\$(1 = 1)")
PrintL Evaluate("1=0")
PrintL Evaluate("""1""")
PrintL Evaluate("Format\$(Len(Parse\$(""hello, world"","" "", 1)))")
PrintL Evaluate("Parse\$(""hello, world"","" "", 1)))")
PrintL Evaluate("15>5")
PrintL Evaluate("15+sin(15)/(10^2)")

WaitKey

Function Evaluate(strng As String) As String
Dim expression, original As String
expression = ("Str\$(" & strng & ") & """"")
expression = Eval_String(expression)
original = Eval_String(strng)
If Format\$(Val(Trim\$(original))) <> Trim\$(original) Then Return original
Return expression
End Function

peralta_mike
10-05-2011, 20:42
Hello,

Is there any way that EVAL can be extended to work with any of the thinbasic functions?

Or can I create functions that EVAL will work with (in addition to the ones already there)?

- Mike Peralta

ErosOlmi
10-05-2011, 21:31
No, I'm sorry.

Eval is just a small interpreter inside an interpreter.

Maybe in future I will be able to extend in such a way script functions can be used inside Eval but at the moment not.