Runtime evaluation: Difference between revisions

From Rosetta Code
Content added Content deleted
(add Ruby)
Line 185: Line 185:
'''
'''
10</lang>
10</lang>

=={{header|Ruby}}==

The <tt>eval</tt> method evaluates a string as code and returns the resulting object. With one argument, it evaluates in the current context:
<lang ruby>a, b = 5, -7
ans = eval "(a * b).abs" # => 35</lang>

With two arguments, <tt>eval</tt> runs in the given <tt>Binding</tt> or <tt>Proc</tt> context:
<lang ruby>def first(main_var, main_binding)
foo = 42
second [[main_var, main_binding], ["foo", binding]]
end

def second(args)
sqr = lambda {|x| x**2}
deref(args << ["sqr", binding])
end

def deref(stuff)
stuff.each do |varname, context|
puts "value of #{varname} is #{eval varname, context}"
end
end

hello = "world"
first "hello", binding</lang>
<pre>value of hello is world
value of foo is 42
value of sqr is #<Proc:0x1002ef6c@eval.rb:7></pre>


=={{header|Scheme}}==
=={{header|Scheme}}==

Revision as of 19:22, 17 June 2009

Task
Runtime evaluation
You are encouraged to solve this task according to the task description, using any language you may know.

Demonstrate your language's ability for programs to execute other programs in the language provided at runtime. Show us what kind of programs (or program fragments) are permitted (e.g. expressions vs. statements), how you get values in and out (e.g. environments, arguments, return values), if applicable what lexical/static environment the program is evaluated in, and what facilities for restricting (e.g. sandboxes, resource limits) or specializing (e.g. debugging facilities) the execution.

You may not invoke a separate evaluator program, or invoke a compiler and then its output, unless the interface of that program, and the syntax and means of executing it, are considered part of your language/library/platform.

For a more restricted task, see Eval in environment.

ALGOL 68

This example is incorrect. Please fix the code and remove this message.

Details: It does not discuss passing in values or the environment the expression is evaluated in.

Works with: ALGOL 68G version Any - tested with release mk15-0.8b.fc9.i386 - this implementation is an interpretor, and evaluate is an extension to the standard

Variable names are generally not visible at run time with classic compilers. However ALGOL 68G is an interpretor and it retains this ability. <lang algol>print(evaluate("4.0*arctan(1.0)"))</lang> Output:

+3.14159265358979e  +0

BASIC

Works with: Beta BASIC version 3.0


Works with: SAM BASIC

Evaluating expressions

VAL() function converts string into numeric value. On many Basic implementations, VAL only accepts simple numeric values. However, Sinclair Basic and its derivates such as Beta Basic and SAM Basic accept any expression that evaluates to numeric value.

The following example shows a functon that plots graph of any function f(x). The function is passed in string parameter f$.

100 DEF PROC graph f$
110   LOCAL x,y
120   PLOT 0,90
130   FOR x = -2 TO 2 STEP 0.02
140     LET y = VAL(f$)
150     DRAW TO x*50+100, y*50+90
160   NEXT x
170 END PROC

Usage example:

500 graph "SIN(x) + SIN(x*3)/3"

Executing code

The KEYIN statement available on Beta Basic and SAM Basic executes a string as if it had been entered from keyboard in command mode. It can execute commands directly, or add (or replace) lines in the program while the program is executing. This allows creating self-modifying programs.

The function do_with_x in the following example loops variable x from 1 to 10 and within the loop executes any code passed to function in parameter p$.

100 DEF PROC do_with_x p$
110   LOCAL x
130   FOR x = 1 TO 10
140     KEYIN p$
160   NEXT x
170 END PROC

The usage example below creates a multiplication table by executing inner loop for y:

500 LET y$ = "FOR y=1 TO 10: PRINT AT y, x*3; x*y: NEXT y"
510 do_with_x y$

VAL and KEYIN execute code in the environment they are called from. In the above examples, VAL and KEYIN both see the local variable x. There is no sandbox functionality in Bata BASIC or SAM BASIC.

Common Lisp

<lang lisp>(eval '(+ 4 5))</lang>

returns 9.

In Common Lisp, programs are represented as trees (s-expressions). Therefore, it is easily possible to construct a program which includes externally specified values, particularly using backquote template syntax:

<lang lisp>(defun add-four-complicated (a-number)

 (eval `(+ 4 ',a-number)))</lang>

Or you can construct a function and then call it. (If the function is used more than once, it would be good to use compile instead of eval, which compiles the code before returning the function. eval is permitted to compile as well, but compile requires it.)

<lang lisp>(defun add-four-by-function (a-number)

 (funcall (eval '(lambda (n) (+ 4 n)))) a-number)</lang>

If your program came from a file or user input, then you have it as a string, and read or read-from-string will convert it to s-expression form: <lang lisp>(eval (read-from-string "(+ 4 5)"))</lang>

Common Lisp has lexical scope, but eval always evaluates “in the null lexical environment”. In particular, it does not inherit the lexical environment from the enclosing code. (Note that eval is an ordinary function and as such does not have access to that environment anyway.)

<lang lisp>(let ((x 1))

 (eval `(+ x 1)))   ; this will fail unless x is a special variable or has a dynamic binding</lang>

There are no sandboxing facilities in standard Common Lisp. Because CL has so many shared mutable objects (packages and symbols), no attempt to design a CL sandbox (other than at the OS process level) has yet succeeded.

There are no standardized debugging facilities specific to the eval operation itself, but code evaluted may be affected by the current global declarations, particularly the optimize declaration's debug and safety qualities.

Groovy

Each of these solutions evaluates a Groovy script based on some variation of the solution to the "Yuletide Holiday" task. Each variation has been verified to give the same output:

[2011, 2016, 2022, 2033, 2039, 2044, 2050, 2061, 2067, 2072, 2078, 2089, 2095, 2101, 2107, 2112, 2118]

Simple evaluation

The GroovyShell class allows the evaluation of a string or of the text contents of a File or InputStream as a Groovy script. A script is a either a set of statements to be executed in order, or a Groovy class with a main() method, or a Groovy Thread subclass or Runnable implementation. The return value is the value of the last statement executed, or the value of an explicit return statement (if any). <lang groovy>def years1 = new GroovyShell().evaluate( (2008..2121).findAll {

   Date.parse("yyyy-MM-dd", "${it}-12-25").format("EEE") == "Sun"

} )

println years1</lang>

The last expression evaluated in the script, a list of years found, is the return value of the evaluate() method.

Evaluation with variables

There are several approaches to evaluating a script with variables:

  • GString embedded values
  • Binding variables
  • Eval shortcut

GString embedded values
Setting up the script as a GString with embedded value parsing is a "natural" ad hoc solution for Groovy programmers, but there are possible pitfalls if the script itself contains GStrings. <lang groovy>def startYear = 2008 def endYear = 2121 def years2 = new GroovyShell().evaluate(""" (${startYear}..${endYear}).findAll {

   Date.parse("yyyy-MM-dd", "\${it}-12-25").format("EEE") == "Sun"

} """)

println years2</lang> The variables "startYear" and "endYear" are dynamically pulled into the script GString as embedded values before the script itself ever executes.

Notice that in the script the embedded value "${it}" must be quoted with backslash (\) to prevent parsing as a part of the script GString. However, it is still correctly parsed within the internal GString when the script is run.

Binding variables
GroovyShell uses a Binding object to pass variable values to a script. This is the only way to pass variables if the script comes from a File or InputStream, but even if the script is a string Binding avoids the nested quoting issue caused by the ad hoc use of GString. <lang groovy>def context = new B inding() context.startYear = 2008 context.endYear = 2121 def years3 = new GroovyShell(context).evaluate( (startYear..endYear).findAll {

   Date.parse("yyyy-MM-dd", "${it}-12-25").format("EEE") == "Sun"

} )</lang>

We may instantiate Binding with the variables as named parameters, allowing a more terse syntax: <lang groovy>def years4 = new GroovyShell( new B inding(startYear: 2008, endYear: 2121) ).evaluate( (startYear..endYear).findAll {

   Date.parse("yyyy-MM-dd", "${it}-12-25").format("EEE") == "Sun"

} )

println years4</lang>

We may also access the Binding object after script evaluation to extract values of any global variables set during the evaluation: <lang groovy>def binding = new B inding(startYear: 2008, endYear: 2121) new GroovyShell( binding ).evaluate( yearList = (startYear..endYear).findAll {

   Date.parse("yyyy-MM-dd", "${it}-12-25").format("EEE") == "Sun"

} )

println binding.yearList</lang>

Eval shortcut
For simple evaluation of string-based scripts with only a few variables (like this one), the Eval class has static shortcut methods that do the Binding setup and GroovyShell evaluation under the surface. Eval.me(script) evaluates a script with no variables. Eval.x(x,script), Eval.xy(x,y,script), or Eval.xyz(x,y,z,script) each evaluates a script with 1, 2, or 3 variables, respectively. Here is an example with start and end years as script variables x and y. <lang groovy>def years5 = Eval.xy(2008, 2121, (x..y).findAll {

   Date.parse("yyyy-MM-dd", "${it}-12-25").format("EEE") == "Sun"

} )

println years5</lang>

Perl

This example is incorrect. Please fix the code and remove this message.

Details: It does not discuss passing in or returning values, or the environment the expression is evaluated in.

<lang perl>eval '4 + 5'</lang>

Python

The exec statement allows the optional passing in of global and local names via mappings (See the link for full syntax). The example below shows exec being used to parse and execute a string containing two statements:

<lang python>>>> exec x = sum([1,2,3,4]) print x 10</lang>

Ruby

The eval method evaluates a string as code and returns the resulting object. With one argument, it evaluates in the current context: <lang ruby>a, b = 5, -7 ans = eval "(a * b).abs" # => 35</lang>

With two arguments, eval runs in the given Binding or Proc context: <lang ruby>def first(main_var, main_binding)

 foo = 42
 second [[main_var, main_binding], ["foo", binding]]

end

def second(args)

 sqr = lambda {|x| x**2}
 deref(args << ["sqr", binding])

end

def deref(stuff)

 stuff.each do |varname, context|
   puts "value of #{varname} is #{eval varname, context}"
 end

end

hello = "world" first "hello", binding</lang>

value of hello is world
value of foo is 42
value of sqr is #<Proc:0x1002ef6c@eval.rb:7>

Scheme

This example is incorrect. Please fix the code and remove this message.

Details: It does not discuss passing in or returning values, or the environment the expression is evaluated in.

This is very similar to the Common Lisp above. <lang scheme>> (eval '(+ 4 5)) 9 > (print (eval (read))) (+ 4 5) ;; this is input from the user. 9</lang>


Slate

In Slate, programs are represented as Syntax Node trees, with methods defined on the various syntactic types. The backtick syntax provides a convenient quoting mechanism, and as objects, they have convenient methods defined for evaluation or evaluation within a specific environment: <lang slate> `(4 + 5) evaluate. `(4 + 5) evaluateIn: prototypes. </lang>

You can also explicitly invoke the Parser on a String, to convert it into syntactic objects: <lang slate> (Syntax Parser newOn: '4 + 5') upToEnd do: [| :each | print: each evaluate] </lang>

You can construct a program using externally-specified values using `unquote within a quoted expression: <lang slate> define: #x -> 4. `(x `unquote + 5) evaluate. </lang>

Or you can obviously construct a string: <lang slate> define: #x -> 4. (Syntax Parser newOn: x printString ; ' + 5') </lang>

The evaluate method also takes into consideration the current lexical scope, unless another environment is specified. The following returns 10, no matter what binding x has in the local namespace: <lang slate> define: #x -> 4. [| x | x: 5. `(x `unquote + 5) evaluate] do. </lang>

Slate can sandbox via constructing a fresh namespace and evaluating within it, but this mechanism is not strongly secure yet.

Smalltalk

This example is incorrect. Please fix the code and remove this message.

Details: It does not discuss passing in or returning values, or the environment the expression is evaluated in., and it does not show taking in an arbitrary program as from user input.

<lang smalltalk> [ 4 + 5 ] value.</lang>

Tcl

Simple Evaluation

Evaluation in the current interpreter: <lang tcl>set four 4 set result1 [eval "expr {$four + 5}"]  ;# string input

set result2 [eval [list expr [list $four + 5]]] ;# list input</lang>

Evaluation in a restricted context

Tcl handles sandboxing by creating new interpreters. Each interpreter is strongly isolated from all other interpreters except in that the interpreter that creates a sub-interpreter retains management control over that “slave” interpreter. The exact capabilities exposed in the slave are controlled by what commands exist in it; commands in the slave may be aliases for other commands in the master interpreter, which allows for trapping into a more highly authorized context (which can be considered analogous to a system call to an OS kernel). <lang tcl># Create an interpreter with a default set of restrictions interp create -safe restrictedContext

  1. Our secret variable

set v "secret"

  1. Allow some guarded access to the secret from the restricted context.

interp alias restrictedContext doubleSecret {} example proc example {} {

   global v
   lappend v $v
   return [llength $v]

}

  1. Evaluate a script in the restricted context

puts [restrictedContext eval {

   append v " has been leaked"
   catch {file delete yourCriticalFile.txt} ;# Will be denied!
   return "there are [doubleSecret] words in the secret: the magic number is [expr {4 + 5}]"

}]; # --> there are 2 words in the secret: the magic number is 9 puts $v; # --> secret secret</lang> As can be seen, the result of the overall evaluation is the same as the result of the evaluation in the slave.

Note that with providing values to the restricted context, it is normal to do this by providing an alias/trap command in the restricted context to allow the script to pick up the value when it wants it. Although the value could also have been provided by setting a variable in the restricted context, this is fairly unusual in practice. The example above shows how this might be done with the result of the doubleSecret command.

Evaluation within limits

Works with: Tcl version 8.5


Even stronger protection of the master interpreter is available from Tcl 8.5 onwards through the setting of resource limits on the slaves. These allow the master to prevent the evaluated script from going berserk: <lang tcl>set i [interp create] interp limit $i commands -value [expr [$i eval info cmdcount]+20] -granularity 1 interp eval $i {

   set x 0
   while {1} { # Infinite loop! Bwahahahaha!
       puts "Counting up... [incr x]"
   }

}</lang> This produces the output (the last line is an error message):

Counting up... 1
Counting up... 2
Counting up... 3
Counting up... 4
Counting up... 5
Counting up... 6
Counting up... 7
Counting up... 8
Counting up... 9
Counting up... 10
command count limit exceeded