Empty interpreter

Difference between version 12 and 14 - Previous - Next
An '''empty interpreter''' is a Tcl interpreter where there are neither commands nor variables.

The following creates an empty slave [interp] '''empty''' for you to experiment with:
 interp create -safe empty
 empty eval {namespace delete ::}
(The [namespace delete] trick here is by [DGP], first published on the [Braintwisters] page.) '''empty''' now exhibits the following behaviour:
 % empty eval {info patchlevel}
 invalid command name "info"
 % empty eval {namespace exists ::}
 invalid command name "namespace"
and so on.

What good is such an interpreter? Well, it is still possible to create aliases in it:
 % empty alias list list {[list] in empty interp}    
 list
 % empty alias foo::bar::baz list {Alias in nontrivial namespace}    
 foo::bar::baz
 % empty eval {list a b c}
 {[list] in empty interp} a b c
 % empty eval foo::bar::foo
 invalid command name "foo::bar::foo"
 % empty eval foo::bar::baz
 {Alias in nontrivial namespace}
Hence empty interpreters can be used as [config file parser]s, for config files that follow Tcl syntax: For every allowed "config file command" you create that command as an alias to something in the main interpreter which actually does what that command is supposed to do. Then [source] or [interp eval] the config file in the empty interpreter, to have it safely processed.
The code on [Matthias Hoffmann - Tcl-Code-Snippets - Misc - Readprof] has been using an empty interpreter in this way at least since 2004.

The following are features you get "for free" by using Tcl for config file syntax:
   * Comments
   * Character escapes, so you can express arbitrary data
   * Newlines separate commands — local changes have local effect
   * Error info (that as of Tcl 8.5 includes line numbers)
Character escapes is something you can have also with a list/dict-based syntax (e.g. the entire file is a dictionary, quite possibly nested), but comments are tricky in that case, and simple typo somewhere can throw off the entire key–value matching throughout the file.

''How can I [source] the file when the [source] command is gone?'' you may ask? Well, as it turns out, the [source] command (and a few others) aren't really gone, since they were [interp hidden] from the [namespace delete] that emptied the interpreter:
 % empty hidden
 file socket open unload pwd glob exec encoding fconfigure load source exit cd
 % empty invokehidden source my-config-file
 couldn't read file "my-config-file": no such file or directory

----
Maybe some day someone will code up a full example of how to use these nice features of Tcl to allow the reading and creating of a config file, taking care of various safety issues. That would make a great example for people.

[AMG]: Wish granted!  See [Config file using slave interp].

[Lars H]: OK, here's a [chatlog reaper] rewritten to use an empty interpreter for parsing, rather than just blindly evaluating whatever code http://tclers.tk/ is sending. These chatlogs are ''supposed'' to only use the '''m''' command, but in an empty interpreter you can make sure that this is all there is to use.
======
#!/usr/bin/env tclsh
set usage {
   usage: today.tcl > today.html
   Retrieve today's chat log, format into HTML on stdout
}
set base http://tclers.tk/conferences/tcl/

if {$argv eq "-h"} {puts $usage; exit 1}

# Make empty interp [chatlog] for parsing
interp create -safe chatlog
chatlog eval {namespace delete ::}

#-- All chat entries invoke [m]
chatlog alias m html_m

proc html_m {time who what} {
   set tm [string range $time 9 13]
   if {$who eq "ijchain"} {
       set who [lindex [split $what] 0]
       if {$who eq "***"} {set who ""}
       set what [join [lrange [split $what] 1 end]]
   }
   set what [string map {\n <br/>} [html'escape $what]]
   if {$who eq ""} {                      #-- join/leave message
       puts "<br/>* <i>$what</i>"
   } elseif {[string match /me* $what]} { #-- "action"
       puts "<br/><i><b>[html'escape $who]</b> [string range $what 4 end]</i>"
   } else {                               #-- regular message
       if {$tm ne $::lasttime} {
           set who "$tm $who"
           set ::lasttime $tm
       }
       puts "<br/><b>[html'escape $who]:</b> $what"
   }
}

set map {}
foreach {char entity} {< lt > gt & amp} {
    lappend map $char &${entity}\;
}
interp alias {} html'escape {} string map $map


proc main argv {
   package require http
   set date [clock format [clock sec] -format %Y-%m-%d]
   #http::config -proxyhost proxy -proxyport 80
   set token [http::geturl $::base/$date.tcl -headers {Pragma no-cache}]

   puts "<html><head>Tclers' Chat of $date</head><body>"
   variable lasttime 00:00
   chatlog eval [http::data $token]
   puts <hr/>[clock format [clock sec]]</body></html>
}

main $argv
======
(Also, this has been slightly rewritten to collect more of the [HTML] handling in the '''m''' proc, to simplify supporting other output formats later.)

----
How do empty interps differ from [safe interps]?
A safe interp cannot access the file system or do any networking, but it still provides a Turing-complete programming environment, which is overkill if the interpreter is meant to just parse data. If no commands for e.g. loops are available, then there is no way for a programming mistake to create an infinite loop.

Also, there is at least presently (July 2008) a bug ([http://sourceforge.net/tracker/index.php?func=detail&aid=1835292&group_id=10894&atid=110894%|%#1835292]) affecting safe interpreters: ensemblification of core commands have made it difficult to [interp hide] their functionality. In an empty interpreter, the commands implementing these subcommands have been outright deleted.
----
A useful extension to DGP's [namespace delete] trick is to safeguard selected (top-level) commands from deletion using [interp hide]/[interp expose]. All credits to DGP on IRC. For example, protect [expr], to obtain an [expr]-only interp:
 % interp create slave
 slave
 % interp hide slave expr expr
 % interp eval slave {namespace delete ::}
 % interp expose slave expr expr
This works because hidden commands are maintained outside the namespace tree. Useful also to reuse the [expr] parser, master procs [interp alias]'ed into ::tcl::mathfunc can be scripted using [expr] notation, rather than Tcl's.


<<categories>> Parsing | Control Structure | Security | Safe Interpreters