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 "