Runtime evaluation: Difference between revisions
→{{header|Common Lisp}}: Oh yes you can do sandboxing in Lisp, if you understand the package system, and what you're doing. Sorry if this is too long. Break off into a page? |
|||
Line 112: | Line 112: | ||
=={{header|Common Lisp}}== |
=={{header|Common Lisp}}== |
||
===Brief <code>eval</code> tutorial=== |
|||
The <tt>eval</tt> function evaluates Lisp code at runtime. |
The <tt>eval</tt> function evaluates Lisp code at runtime. |
||
Line 141: | Line 143: | ||
(format t "~%x + y = ~a" (eval `(+ ,x ,y))))</lang> |
(format t "~%x + y = ~a" (eval `(+ ,x ,y))))</lang> |
||
===Sandboxing Discussion=== |
|||
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<!-- ; see e.g. [telnet://prompt.franz.com prompt.franz.com] (not mentioning because this seems to be down now)-->) has yet succeeded. |
|||
Sandboxing in Common Lisp can be approached in a variety of ways, none of which are standardized. |
|||
One approach to define a sublanguage and validate expressions before passing them to <code>compile</code> or <code>eval</code>. Of course, a whole different language entirely can be defined, and translated to Lisp. This is essentially the classic "trusted compiler generating safe code in an untrusted target language" approach. |
|||
One way to simplify the validator is to use the package system to create a sandbox. This is done by defining a package arbitrarily called <code>sandbox</code>. The validator then simply has to make sure that all symbols used in the expression are restricted to those which are visible inside the <code>sandbox</code> package. Inside <code>sandbox</code>, we include only those functions, operators, variables and other symbols from system packages that are safe: materials which don't allow sandboxed code to do anything harmful from within the sandbox, or to escape from the sandbox. For instance, suppose that some package <code>system</code> has a function called <code>run-shell-command</code>. We do not import <code>run-shell-command</code> into the <code>sandbox</code> package, and our validator will reject code which has references such as <lang lisp>(system:run-shell-command ...)</lang>. Therefore, the sandboxed code has no direct way to run that function. To gain access to it, it must exploit some flaw in the sandbox. One flaw in the sandbox would be the inclusion of certain package-related functions like <code>find-symbol</code>. The expression <lang lisp>(find-symbol "FOO" "BAR")</lang> will retrieve symbol <code>foo::bar</code> if it exists. The validator will not find this code because it has no embedded symbolic references to package <code>foo</code>; they are disguised as character string. A cautious approach to the sandbox should be taken: include less rather than more, and consider each expansion of the sandbox with meticulous care. |
|||
===Debugging Notes=== |
|||
There are no standardized debugging facilities specific to the <code>eval</code> operation itself, but code evaluted may be affected by the current [http://www.lispworks.com/documentation/HyperSpec/Body/03_c.htm global declarations], particularly the [http://www.lispworks.com/documentation/HyperSpec/Body/d_optimi.htm <code>optimize</code> declaration]'s <code>debug</code> and <code>safety</code> qualities. |
There are no standardized debugging facilities specific to the <code>eval</code> operation itself, but code evaluted may be affected by the current [http://www.lispworks.com/documentation/HyperSpec/Body/03_c.htm global declarations], particularly the [http://www.lispworks.com/documentation/HyperSpec/Body/d_optimi.htm <code>optimize</code> declaration]'s <code>debug</code> and <code>safety</code> qualities. |
||
Revision as of 00:05, 27 September 2011
![Task](http://static.miraheze.org/rosettacodewiki/thumb/b/ba/Rcode-button-task-crushed.png/64px-Rcode-button-task-crushed.png)
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 code written in the language provided at runtime. Show us what kind of 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 customizing (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 constrained task giving a specific program fragment to evaluate, see Eval in environment.
ALGOL 68
Variable names are generally not visible at run time with classic compilers. However ALGOL 68G is an interpretor and it retains this ability. <lang algol68>print(evaluate("4.0*arctan(1.0)"))</lang> Output:
+3.14159265358979e +0
AutoHotkey
function addScript can be used to dynamically add lines of code to a running script. <lang AutoHotkey>; requires AutoHotkey_H or AutoHotkey.dll msgbox % eval("3 + 4") msgbox % eval("4 + 4") return
eval(expression)
{
global script
script =
(
expression(){ return %expression% }
) renameFunction("expression", "") ; remove any previous expressions gosub load ; cannot use addScript inside a function yet exp := "expression" return %exp%() }
load: DllCall(A_AhkPath "\addScript","Str",script,"Uchar",0,"Cdecl UInt") return
renameFunction(funcName, newname){ static x%newname% := newname ; store newname in a static variable so its memory is not freed strput(newname, &x%newname%, strlen(newname) + 1) if fnp := FindFunc(funcName)
numput(&x%newname%, fnp+0, 0, "uint")
}</lang>
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.
In ZX Spectrum Basic, loading a new program will replace the existing program. The new program will sutomatically run, if it was saved to do so by using SAVE together with LINE:
<lang zxbasic> 10 REM load the next program 20 LOAD "PROG2" </lang>
Common Lisp
Brief eval
tutorial
The eval function evaluates Lisp code at runtime.
<lang lisp>(eval '(+ 4 5)) ; returns 9</lang>
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, eval
does not inherit the lexical variables 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 11) (y 22))
;; This is an error! Inside the eval, x and y are unbound! (format t "~%x + y = ~a" (eval '(+ x y))))</lang>
One way to fix the error is to (declare (special x y)) for dynamic variables; but the easier and shorter way is to insert the values of x and y with the backquote template syntax.
<lang lisp>(let ((x 11) (y 22))
(format t "~%x + y = ~a" (eval `(+ ,x ,y))))</lang>
Sandboxing Discussion
Sandboxing in Common Lisp can be approached in a variety of ways, none of which are standardized.
One approach to define a sublanguage and validate expressions before passing them to compile
or eval
. Of course, a whole different language entirely can be defined, and translated to Lisp. This is essentially the classic "trusted compiler generating safe code in an untrusted target language" approach.
One way to simplify the validator is to use the package system to create a sandbox. This is done by defining a package arbitrarily called sandbox
. The validator then simply has to make sure that all symbols used in the expression are restricted to those which are visible inside the sandbox
package. Inside sandbox
, we include only those functions, operators, variables and other symbols from system packages that are safe: materials which don't allow sandboxed code to do anything harmful from within the sandbox, or to escape from the sandbox. For instance, suppose that some package system
has a function called run-shell-command
. We do not import run-shell-command
into the sandbox
package, and our validator will reject code which has references such as <lang lisp>(system:run-shell-command ...)</lang>. Therefore, the sandboxed code has no direct way to run that function. To gain access to it, it must exploit some flaw in the sandbox. One flaw in the sandbox would be the inclusion of certain package-related functions like find-symbol
. The expression <lang lisp>(find-symbol "FOO" "BAR")</lang> will retrieve symbol foo::bar
if it exists. The validator will not find this code because it has no embedded symbolic references to package foo
; they are disguised as character string. A cautious approach to the sandbox should be taken: include less rather than more, and consider each expansion of the sandbox with meticulous care.
Debugging Notes
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.
E
In E, eval is a method of expression ASTs (EExprs). (Other types of program fragment ASTs such as methods and patterns may not be directly evaluated, and must be inserted into an expression.)
The lexical environment is provided as a parameter and cannot be omitted. The evaluated program has no access to anything but the provided environment.
<lang e>? e`1 + 1`.eval(safeScope)
- value: 2</lang>
eval
returns the value of the expression. evalToPair
also returns the modified environment for use with further evaluation, e.g. for implementing a REPL.
<lang e>? def [value, env] := e`def x := 1 + 1`.evalToPair(safeScope)
- value: [2, ...]
? e`x`.eval(env)
- value: 2</lang>
Eval from a string may be done by invoking the parser.
<lang e>? def prog := <elang:syntax.makeEParser>.run("1 + 1")
- value: e`1.add(1)`
? prog.eval(safeScope)
- value: 2</lang>
Erlang
Erlang eval is a bit complex/verbose and requires the interaction of 3 modules: erl_scan (tokenizes), erl_parse (returns an abstract form) and erl_eval (variable binding, evaluate abstract form, etc). <lang erlang>1> {ok, Tokens, _} = erl_scan:string("X + 4 * lists:sum([1,2,3,4])."). ... 2> {ok, [Form]} = erl_parse:parse_exprs(Tokens). ... 3> Bindings = erl_eval:add_binding('X', 17, erl_eval:new_bindings()). [{'X',17}] 4> {value, Value, _} = erl_eval:expr(Form, Bindings). {value,57,[{'X',17}]}</lang>
Forth
EVALUATE invokes the interpreter on a string of Forth code, using and modifying the current dictionary and stack state. <lang forth>s" variable foo 1e fatan 4e f*" evaluate
f. \ 3.14159... 1 foo !</lang>
Sandboxing can be achieved in general by using MARKER, which defines a checkpoint for the dictionary state which can later be restored.
<lang forth>unused . \ show how much dictionary space is available marker restore
create foo 30 allot
- my-def 30 0 do cr i . ." test" loop ;
unused . \ lower than before
restore unused . \ same as first unused; restore, foo, and my-def no longer defined</lang>
Go
As a compiled, strongly typed language, eval()
is not the strong suit of Go. Nevertheless, an eval
package exists that does that. Just don't expect it to be as easy or efficient as in interpreted languages. The eval package was originally part of the Go standard library but is now hosted and maintained externally.
<lang Go>package main
import (
"fmt"
"bitbucket.org/binet/go-eval/pkg/eval"
"go/token"
)
func main() { w := eval.NewWorld(); fset := token.NewFileSet();
code, err := w.Compile(fset, "1 + 2") if err != nil { fmt.Println("Compile error"); return }
val, err := code.Run(); if err != nil { fmt.Println("Run time error"); return; } fmt.Println("Return value:", val) //prints, well, 3
}</lang>
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>
GW-BASIC
<lang qbasic>10 LINE INPUT "Type an expression: ",A$ 20 OPEN "CHAIN.TMP" FOR OUTPUT AS #1 30 PRINT #1, "70 LET Y=("+A$+")" 40 CLOSE #1 50 CHAIN MERGE "CHAIN.TMP",60,ALL 60 FOR X=0 TO 5 70 REM 80 PRINT X,Y 90 NEXT X 100 GOTO 10</lang>
HicEst
XEQ invokes the interpreter on a string of HicEst code, but keeps the current dictionary and stack state. Blocks of expressions are not possible. <lang HicEst>value = XEQ( " temp = 1 + 2 + 3 ") ! value is assigned 6 ! temp is undefined outside XEQ, if it was not defined before.
XEQ(" WRITE(Messagebox) 'Hello World !' ")
OPEN(FIle="my_file.txt") READ(FIle="my_file.txt", Row=6) string XEQ( string ) ! executes row 6 of my_file.txt</lang>
J
Use monadic ".
(Do) to execute a string.
<lang j>". 'a =: +/ 1 2 3' NB. execute a string to sum 1, 2 and 3 and assign to noun a</lang>
Only J expressions are allowed in strings used as as arguments for ".
(control words and blocks of expressions are not allowed).
Alterntively, you can use the conjunction :
(Explicit Definition) to create various kinds of functions and evaluate them. Arguments have names, such as "y", which are specified by the language definition. For example:
<lang j>monad :'+/y' 1 2 3</lang>
Rules of scope for such functions match those described on the Scope modifiers page. Also, control words (like if. or for. or while.) and blocks of expressions are allowed in strings which are evaluated in this fashion.
The context for these evaluations will always be the current locale (which might typically be the current object [or class]). If only expressions are allowed, then local variables will be local to the current explicit definition. Otherwise a new local context will be created for the evaluation (and this will be discarded when evaluation has completed). Local contexts are lexical while locales may also be manipulated programatically.
Debugging facilities [currently] require that the operation be given a name.
J relies on the OS for sandboxing and does not offer any additional resource constraints.
Lua
<lang lua>f = loadstring(s) -- load a string as a function. Returns a function.
one = loadstring"return 1" -- one() returns 1
two = loadstring"return ..." -- two() returns the arguments passed to it</lang>
JavaScript
The eval method handles statements and expressions well: <lang javascript> var foo = eval('{value: 42}'); eval('var bar = "Hello, world!";');
typeof foo; // 'object' typeof bar; // 'string' </lang>
Oz
<lang oz>declare
%% simplest case: just evaluate expressions without bindings R1 = {Compiler.virtualStringToValue "{Abs ~42}"} {Show R1}
%% eval expressions with additional bindings and %% the possibility to kill the evaluation by calling KillProc KillProc R2 = {Compiler.evalExpression "{Abs A}" unit('A':~42) ?KillProc} {Show R2}
%% full control: add and remove bindings, eval expressions or %% statements, set compiler switches etc. Engine = {New Compiler.engine init} {Engine enqueue(setSwitch(expression false))} %% statements instead of expr. {Engine enqueue(mergeEnv(env('A':42 'System':System)))} {Engine enqueue(feedVirtualString("{System.show A}"))}</lang>
By restricting the environment it is possible to restrict what kind of programs can be run.
Perl
The eval
function accepts a block or a string as its argument. The difference is that a block is parsed at compile-time, whereas a string is parsed at runtime. The block or string may represent any valid Perl program, including a single expression. The subprogram executes in the same lexical and dynamic scope as the surrounding code. The return value of a call to eval
depends on how the subprogram terminates:
- If control reaches the end of the subprogram,
eval
returns the value of the last expression evaluated. - If the subprogram uses an explicit
return
,eval
returns the given value. - If the subprogram throws an exception,
eval
returnsundef
. The text of the exception is assigned to$@
. (When the subprogram terminates without an exception,$@
is set to the null string instead.)
<lang perl>my ($a, $b) = (-5, 7); $ans = eval 'abs($a * $b)'; # => 35</lang>
Perl 6
Any syntactically valid sequence of statements may be run, and the snippet to be run can see its outer lexical scope at the point of the eval: <lang perl>my ($a, $b) = (-5, 7); my $ans = eval 'abs($a * $b)'; # => 35</lang> Unlike in Perl 5, eval in Perl 6 only compiles and executes the string, but does not trap exceptions. You must say try eval to get that behavior (or supply a CATCH block within the text to be evaluated).
PHP
The eval construct allow string evaluation as PHP code. Opening and closing tags are not required. Return statements immediatly terminates evaluation . Eval returns NULL, unless return is called in evalued code. <lang php> <?php
$code = 'echo "hello world"'; eval($code); $code = 'return "hello world"'; print eval($code);
</lang>
PicoLisp
In PicoLisp there is a formal equivalence of code and data. Almost any peace of data is potentially executable. PicoLisp has three internal data types: Numbers, symbols and lists. Though in certain contexts (e.g. GUI objects) also atomic data (numbers and symbols) are evaluated as code entities, a typical executable item is a list.
The PicoLisp reference distinguishes between two terms: An 'exe' (expression) is an executable list, with a function as the first element, followed by arguments. A 'prg' (program) is a list of 'exe's, to be executed sequentially.
'exe's and 'prg's are implicit in the whole runtime system. For example, the body of a function is a 'prg', the "true" branch of an 'if' call is an 'exe', while the "false" branch again is a 'prg'.
For explicit execution, an 'exe' can be evaluated by passing it to the function 'eval', while a 'prg' can be handled by 'run'.
As PicoLisp uses exclusively dynamic binding, any 'exe' or 'prg' can be executed in arbitrary contexts. The environmet can be controlled in any conceivable way, through implicit function parameter bindings, or explicitly with the aid of functions like 'bind', 'let' or 'job'.
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>
Note that in Python 3.x exec is a function:
<lang python>>>> exec( x = sum([1,2,3,4]) print(x) ) 10</lang>
R
In R, expressions may be manipulated directly as abstract syntax trees, and evaluated within environments.
quote() captures the abstract syntax tree of an expression. parse() does the same starting from a string. call() constructs an evaluable parse tree. Thus all these three are equivalent.
<lang r>expr1 <- quote(a+b*c) expr2 <- parse(text="a+b*c")1 expr3 <- call("+", quote(`a`), call("*", quote(`b`), quote(`c`)))</lang>
eval() evaluates a quoted expression. evalq() is a version of eval() which quotes its first argument.
<lang r>> a <- 1; b <- 2; c <- 3 > eval(expr1) [1] 7</lang>
eval() has an optional second environment which is the lexical environment to evaluate in.
<lang r>> env <- as.environment(list(a=1, b=3, c=2)) > evalq(a, env) [1] 1 > eval(expr1, env) #this fails; env has only emptyenv() as a parent so can't find "+" Error in eval(expr, envir, enclos) : could not find function "+" > parent.env(env) <- sys.frame() > eval(expr1, env) # eval in env, enclosed in the current context [1] 7 > assign("b", 5, env) # assign() can assign into environments > eval(expr1, env) [1] 11</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
In Scheme, the expression passed to eval is evaluated in the current interaction environment, unless otherwise specified. The result is read back as a Scheme value.
<lang scheme>> (define x 37) > (eval '(+ x 5)) 42 > (eval '(+ x 5) (interaction-environment)) 42 > (eval '(+ x 5) (scheme-report-environment 5)) ;; provides R5RS definitions
Error: identifier not visible x. Type (debug) to enter the debugger. > (display (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
<lang smalltalk>[ 4 + 5 ] value.</lang> Evaluating a block given as a string: (Squeak, Pharo) <lang smalltalk>e := '[ :x :y | (x*x + (y*y)) sqrt ]'. Transcript show: ((Compiler evaluate: e) value: 3 value: 4).</lang>
SNOBOL4
The built in function eval() evaluates SNOBOL4 expressions and returns the value. The expression is evaluated in the current environment and has access to then-current variables.
<lang snobol4> expression = "' page ' (i + 1)"
i = 7 output = eval(expression)
end</lang>
Output:
page 8
The built in function code() compiles complete SNOBOL4 source statements, or even complete programs. The compiled program is returned (as a value of type CODE), and when executed the program is executed in the then-current environment and has access to the then-current variables. Labels in the compiled program are added to the current program. Programs of type CODE are executed by a variant of the goto clause:
<lang SNOBOL4> compiled = code(' output = "Hello, world."') :s<compiled> end</lang>
When passing programs to code(), semicolons are used to separate lines.
The calling (already-compiled) program can call, for example, functions that are defined in the code compiled at runtime, and can include gotos to labels only defined in the code compiled at runtime. Likewise, the code compiled at runtime has access to not just variables, but also files, functions, etc., that are in the already-compiled program.
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
- Our secret variable
set v "secret"
- 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]
}
- 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
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
TI-89 BASIC
The function expr(string)
evaluates the string as an expression. It is evaluated in the environment of the calling program (it can see local variables).
The Exec string, args...
statement executes arbitrary 68k machine code (and is thus entirely unsafe).
TODO: Is there a way to execute statements as well as evaluate expressions?
- Programming Tasks
- Solutions by Programming Task
- ALGOL 68
- ALGOL 68 examples needing attention
- Examples needing attention
- AutoHotkey
- BASIC
- Common Lisp
- E
- Erlang
- Forth
- Go
- Groovy
- GW-BASIC
- HicEst
- J
- Lua
- JavaScript
- Oz
- Perl
- Perl 6
- PHP
- PicoLisp
- Python
- R
- Ruby
- Scheme
- Slate
- Smalltalk
- SNOBOL4
- Tcl
- TI-89 BASIC
- TI-89 BASIC examples needing attention
- Ada/Omit
- C/Omit
- C++/Omit
- D/Omit
- Java/Omit
- Pascal/Omit
- PureBasic/Omit