1. eval()

With a bit of restructuring, we can have a simple eval() function in Phix.

-- eval.e -- (approx 90% of the compiler, not an autoinclude, and not in builtins\) 
global function eval(string code, sequence rset={}, iset={}, ival={}) 

Sample use:

requires("1.0.1") 
include eval.e  -- (not an autoinclude, pulls in around 90% of the interpreter/compiler proper) 
 
string code = " ""  
--integer res = 0 
bool res_init = false 
sequence res 
if not res_init then res = {} end if 
for i=1 to 4 do 
--  res += i 
    res &= i 
end for 
"" " 
?eval(code,{"res"})     -- 10 for integer res, else {1,2,3,4} 
--?eval(code,{"res"},{"res"},{5})  -- 15 for integer res 
?eval(code,{"res"},{{"res_init",true},{"res",{5}}}) -- {5,1,2,3,4} 
 
--note: strings such as `\n` (length 2) must be passed to eval,  
--        as opposed to "\n" (length 1), otherwise you'll just 
--        get errors such as "missing closing quote". You can 
--        use backticks or triple-quotes to prevent backslash 
--        interpretation, or build the code strings manually. 
{} = eval(`puts(1,"Hello World\n")`) 

In Phix you must always declare variables before use, and such code fragments are no exception.
It is not expensive to copy variables/data from the calling code into and out of the "eval context" (for want of a better term).

The eval function accepts up to 4 parameters: 
code is the source code fragment to be interpreted, 
rset is a list of string names of (static) result variable identifiers 
iset is a list of string names of (static) variables to initialise 
ival is a list of values matching iset, alternatively ival can be 
omitted and iset instead provided as a list of {name,value} pairs. 
 
Note that static integers are initialised at compile-time and may therefore be 
overidden in iset/ival, whereas strings, sequences, and floating point values 
may need a little more care. For instance sequence res = {} is duty bound to 
overwrite any value in iset/ival whereas bool(/integer) res_init = false and 
if not res_init then res = {} end if in the code snippet can prevent any such 
unwanted overwrite, provided that iset/ival contains (both) res_init and res. 

Of course this cannot possibly work as-is under pwa/p2js, in time perhaps there could be some kind of equivalent for the transpiler, with
transpiled phix code running in a transpiled program, and until there is I will remain rather reluctant to make this a builtin/autoinclude.

One thing that is not yet clear is the lifetime/persistency of generated code. Attempts to set a callback (for instance) will likely
have the rug pulled from under its feet at some indeterminate time in the future, as the code space ends up getting reclaimed...
Maybe an rset[1] of "$" yields a reference the calling code has to cling onto for as long as it needs it.

Comments/thoughts?

new topic     » topic index » view message » categorize

2. Re: eval()

petelomax said...
{} = eval(`puts(1,"Hello World\n")`) 

In Phix you must always declare variables before use, and such code fragments are no exception.

This is a literal string being passed, and so this will not work?

string cmd = `puts(1,"Hello World\n")` 
x = eval(cmd) 
petelomax said...

It is not expensive to copy variables/data from the calling code into and out of the "eval context" (for want of a better term). {{{ The eval function accepts up to 4 parameters: code is the source code fragment to be interpreted, rset is a list of string names of (static) result variable identifiers iset is a list of string names of (static) variables to initialise ival is a list of values matching iset, alternatively ival can be omitted and iset instead provided as a list of {name,value} pairs.

So this feature can be used to explore how a procedure will act on various settings? I can loop thru dynamically incremented values, for instance?

petelomax said...

One thing that is not yet clear is the lifetime/persistency of generated code.

You are saying the data passed to eval() is all saved in ram in a way that it's automatically reclaimed by Phix whenever? I must not understand, i assumed this would be the standard operating situation. Again assuming, if in the code i first posted, i can store the code in variable cmd and simply call eval(cmd) again as i need? Or will this clutter up ram and crash Phix?

A little something i wrote ~25 years ago, on win95, as a line in a database :

<exec> /msg %chan <mirccolor12> The date and time here is: <mirccolor2> $gettok($asctime, 1-3, 32) $gettok($asctime, 5, 32) <mirccolor12> $atime 

petelomax said...

Comments/thoughts?

Well, you certainly left OE in the dust! That said, on a C-64 circa mid-1980's a program could grab some spare ram, copy vars and code needed to include into that ram, edit some pointers, and that would be a new environment, run the isolated sandboxed code, until the code terminated and the program put the pointers back, dropping it back to the original environment.

I am unsure how to say how great this can become, without tripping over my words.

Kat

new topic     » goto parent     » topic index » view message » categorize

3. Re: eval()

"I am unsure how to say how great this can become, without tripping over my words"

That's so true. I love that eval :)

new topic     » goto parent     » topic index » view message » categorize

4. Re: eval()

"I am unsure how to say how great this can become, without tripping over my words"

That's so true. I love that eval :)

new topic     » goto parent     » topic index » view message » categorize

5. Re: eval()

katsmeow said...
string cmd = `puts(1,"Hello World\n")` 
x = eval(cmd) 

That is fine.

Let me try some more examples.
This will not work:

include eval.e 
integer x = 2, y = 2, z 
?eval("z = x + y") 

Output (and yes, it has designated/invented a filename of "", given it a fairly meaningless line number, and not quite sure why it picks the builtins directory):

C:\Program Files (x86)\Phix\builtins\:1 
z = x + y 
^ undefined identifier z 
 
Press Enter, or d for diagnostics... 

This however will:

include eval.e 
?eval("integer x, y, z = x + y",{"z"},{{"x",2},{"y",2}}) -- outputs {4} 

If however we did this:

include eval.e 
{} = eval("integer x, y, z = x + y ?z",{},{{"x",2},{"y",2}}) -- outputs 4 

Then, since we did not ask for z to be returned, it (along with x and y) is gone, for good.
The standard compiler/interpreter wilfully and regularly just "throws stuff away" when execution terminates,
and of course that same effect happens (for now at least) when you embed eval() in another application.
Precisely when than happens is officially "undefined": it would be fair to say that C-based languages have a
"whenever the GC decides to trigger" policy whereas Phix has a "no continuous bleed" policy, and neither have
an "asap" policy, for obvious performance reasons both, though Phix moved about 12 steps closer to the latter
with the the 1.0.0 release, not that 100% asap will ever be an actual target.

Obviously you can do things in loops, eg (noting we have two entirely separate x and y here)

include eval.e 
string cmd = `integer x, y printf(1,"%d + %d = %d\n",{x,y,x+y})` 
for x=1 to 2 do 
    for y=1 to 2 do 
        {} = eval(cmd,{},{{"x",x},{"y",y}}) 
    end for 
end for 

Output:

1 + 1 = 2 
1 + 2 = 3 
2 + 1 = 3 
2 + 2 = 4 

However as mentioned in "the lifetime/persistency of generated code" there may be some performance hitches with recompiling/reinitialising/extracting/restoring values that may or may not need to be addressed, especially when it comes to longer code snippets. It may all be fine, just could possibly deserve some improvement when/if someone starts to repeatedly eval 10,000+ lines.

In contrast, shunting humongous tables into and out of such "eval contexts" should not have any significant overheads, barring perhaps the odd careless and hopefully fairly easily fixable refcount slipup.

There isn't any special syntax here, the code string passed has to be complete enough to run standalone, although we do get the chance to "poke" and "peek" a few variables.

new topic     » goto parent     » topic index » view message » categorize

6. Re: eval()

My mind is cluttered with other stuff at the moment, but that code in your eval() examples looks very different from regular Phix/OE code. I assume it's that way to make the programmer's intent less ambiguous?

new topic     » goto parent     » topic index » view message » categorize

7. Re: eval()

katsmeow said...

My mind is cluttered with other stuff at the moment, but

OK

katsmeow said...

that code in your eval() examples looks very different from regular Phix/OE code.

Uh? The actual "code" in those strings, syntax coloured instead of string coloured, is:

integer x, y, z = x + y 
 
integer x, y, z = x + y ?z 
 
integer x, y printf(1,"%d + %d = %d\n",{x,y,x+y}) 

I suspect it is just the rset/iset handling going on around them that threw you.

new topic     » goto parent     » topic index » view message » categorize

Search



Quick Links

User menu

Not signed in.

Misc Menu