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. [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
} [html'escape $what]] if {$who eq ""} { #-- join/leave message puts "
* $what" } elseif {[string match /me* $what]} { #-- "action" puts "
[html'escape $who] [string range $what 4 end]" } else { #-- regular message if {$tm ne $::lasttime} { set who "$tm $who" set ::lasttime $tm } puts "
[html'escape $who]: $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 "Tclers' Chat of $date" variable lasttime 00:00 chatlog eval [http::data $token] puts
[clock format [clock sec]] } 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. ---- !!!!!! %| [Category Parsing] | [Category Control Structure] | [Category Security] |% !!!!!!