Error processing request

Parameters

CONTENT_LENGTH0
REQUEST_METHODGET
REQUEST_URI/revision/Local+Packages+and+Transparent+Namespaces?V=2
QUERY_STRINGV=2
CONTENT_TYPE
DOCUMENT_URI/revision/Local+Packages+and+Transparent+Namespaces
DOCUMENT_ROOT/var/www/nikit/nikit/nginx/../docroot
SCGI1
SERVER_PROTOCOLHTTP/1.1
HTTPSon
REMOTE_ADDR172.69.6.195
REMOTE_PORT61458
SERVER_PORT4443
SERVER_NAMEwiki.tcl-lang.org
HTTP_HOSTwiki.tcl-lang.org
HTTP_CONNECTIONKeep-Alive
HTTP_ACCEPT_ENCODINGgzip, br
HTTP_X_FORWARDED_FOR13.59.218.147
HTTP_CF_RAY879615d7aff9618d-ORD
HTTP_X_FORWARDED_PROTOhttps
HTTP_CF_VISITOR{"scheme":"https"}
HTTP_ACCEPT*/*
HTTP_USER_AGENTMozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; [email protected])
HTTP_CF_CONNECTING_IP13.59.218.147
HTTP_CDN_LOOPcloudflare
HTTP_CF_IPCOUNTRYUS

Body


Error

Unknow state transition: LINE -> END

-code

1

-level

0

-errorstack

INNER {returnImm {Unknow state transition: LINE -> END} {}} CALL {my render_wikit {Local Packages and Transparent Namespaces} \[SLB\]\ Small\ Tcl\ scripts\ are\ easy\ to\ write\ with\ short\ procedure\ names\nplaced\ in\ the\ root\ namespace.\ As\ scripts\ become\ larger,\ breaking\ them\ninto\ relatively\ self-contained\ components\ which\ can\ be\ re-used\ and\navoiding\ name\ conflicts\ between\ those\ components\ becomes\ important.\nThis\ is\ normally\ done\ with\ namespaces\ and\ packages.\n\nWhile\ the\ namespace\ and\ package\ mechanisms\ work\ reasonably\ well,\nthey\ do\ add\ clutter\ to\ scripts.\ \[http://www.tcl.tk/cgi-bin/tct/tip/189.html\]\nhas\ made\ it\ easier\ to\ write\ packages\ but\ you\ do\ still\ need\ to\ choose\na\ unique\ name\ for\ each\ package.\ Sometimes\ it\ just\ seems\ easier\nto\ do\ something\ like:\n\n\tsource\ \[file\ dirname\ \[info\ script\]\]/OtherScript.tcl\n\nand\ bypass\ the\ use\ of\ packages\ altogether.\n\nYet\ packages\ have\ benefits.\ This\ page\ \ explores\ an\ alternative\ that\ngives\ some\ of\ the\ benefits\ of\ packages\ and\ namespaces\ but\ with\ less\ clutter\nand\ reduced\ risk\ of\ name\ collisions...\n\nThe\ alternative\ is\ based\ on\ a\ 'loader'\ command\ which\ is\ used\ thus:\n\n\tloader\ use\ ?-force?\ ?-cmd\ cmd?\ ?-import?\ moduleName.tcl\n\nIts\ behaviour\ is\ somewhat\ similar\ to\ both\ 'source'\ and\ 'package\ require'.\n\nThe\ full\ pathname\ for\ moduleName.tcl\ is\ obtained\ (see\ later).\nIf\ the\ module\ has\ not\ already\ been\ loaded,\ the\ pathname\ is\ sourced.\ This\ is\ done\nwithin\ the\ context\ of\ a\ dynamically\ allocated\ namespace.\ Thus\ a\ procedure\ndefinition\ 'proc\ a\ \{\}\ \{\ ...\ \}'\ within\ moduleName.tcl\ will\ automatically\nbe\ distinguished\ from\ any\ other\ definition\ of\ 'proc\ a'.\n\nProvided\ the\ moduleName.tcl\ is\ sourced\ without\ error\ (or\ if\ it\ was\ previously\nloaded\ successfully),\ the\ commands\ it\ exports\ are\ made\ available\ to\ the\ client\nscript.\ This\ can\ be\ done\ directly\ by\ importing\ the\ commands\ into\ the\ client's\nnamespace\ (if\ the\ -import\ switch\ was\ specified)\ or\ by\ adding\ an\ ensemble\ncommand\ to\ the\ client's\ namespace\ that\ contains\ the\ exported\ commands.\n\nThe\ mechanism\ for\ determining\ the\ full\ pathname\ of\ a\ module\ loaded\ by\ 'loader\ use'\nis\ of\ course\ based\ on\ a\ search\ path\ of\ directories.\ However,\ it\ does\ not\ require\na\ single\ global\ search\ path.\ Instead,\ 'loader\ use'\ obtains\ the\ namespace\ from\nwhich\ it\ is\ called\ and\ uses\ that\ to\ determine\ the\ full\ pathname\ of\ the\ file\ which\ was\ sourced\nwhen\ the\ namespace\ was\ allocated.\ This\ pathname\ can\ have\ its\ own\ unique\ search\ path\nof\ directories.\ If\ the\ referenced\ module\ name\ is\ not\ found\ within\ the\ module's\nown\ search\ path,\ its\ parent\ directory\ is\ considered.\ The\ mechanism\ does\ not\nlook\ for\ modules\ in\ the\ parent\ directory\ directly,\ instead\ it\ checks\ whether\na\ search\ path\ has\ been\ defined\ for\ the\ directory.\ If\ it\ has\ that\ search\ path\ is\nused.\ The\ search\ then\ proceeds\ through\ all\ parent\ directories\ up\ to\ the\ top\ level.\n\nMost\ directories\ will\ have\ no\ associated\ search\ path,\ so\ use\ of\ a\ deep\ directory\nhierarchy\ will\ not\ trigger\ any\ additional\ file\ i/o.\ Some\ directories\ will\ specify\ntheir\ own\ pathname\ as\ the\ only\ entry\ in\ their\ search\ path.\ A\ few\ may\ have\ a\ search\ path\ncontaining\ other\ directories.\n\nEach\ time\ a\ module\ is\ loaded,\ the\ parent\ directory\ is\ added\ to\ its\ own\ search\ path.\nThis\ is\ a\ bit\ ad-hoc\ but\ seems\ useful.\n\nModules\ can\ be\ made\ globally\ available\ by\ placing\ their\ directory\ in\ the\ search\ path\nfor\ directory\ '/'.\ This\ requires\ a\ little\ special\ handing\ on\ Windows,\ where\npathnames\ such\ as\ 'C:/'\ are\ considered\ top\ level\ directories.\ That\ special\nhandling\ is\ built\ into\ the\ mechanism\ and\ so\ is\ transparent\ to\ its\ clients.\n\nThe\ -force\ switch\ allows\ a\ module\ to\ be\ reloaded.\ In\ such\ cases,\ it\ is\ reloaded\ninto\ the\ same\ namespace\ that\ was\ previously\ allocated\ for\ it.Commands\ that\ are\ not\ contained\ within\ a\ namespace\ allocated\ by\ 'loader\ use'\ncan\ load\ modules\ either\ by\ specifying\ the\ absolute\ path\ of\ the\ module\ or\ by\nreferencing\ modules\ accessible\ from\ the\ search\ path\ for\ directory\ '/'.\n\n\nThe\ result\ of\ all\ this\ is\ that\ you\ can\ create\ a\ collection\ of\ modules\ which\ reference\neach\ other\ using\ short\ descriptive\ names\ such\ as\ Process\ or\ Launcher.\ Such\ modules\ncan\ then\ be\ placed\ unchanged\ into\ a\ 'package'\ (though\ not\ a\ traditional\ Tcl\ package)\ncalled\ MyPackage.\ncan\ then\ be\ placed\ unchanged\ into\ a\ larger\ system\ in\ a\ package\ called\ MyPackage.\nbe\ turned\ into\ a\ package,\ and\ the\ original\ components\ might\ then\ be\ known\ to\nits\ clients\ as\ BigPackage/MyPackage/Process\ and\ BigPackage/MyPackage/Launcher.\nbe\ turned\ into\ a\ package,\ and\ the\ original\ components\ might\ then\ be\ known\ as\nBigPackage/MyPackage/Process\ and\ BigPackage/MyPackage/Launcher.\nversion\ checking.\ Of\ course\ a\ module\ can\ provide\ a\ version\ checking\ command\ that\ clients\nOne\ limitation\ of\ the\ mechanism\ compared\ to\ packages\ is\ that\ there's\ no\ support\ for\n\ncan\ call\ once\ the\ package\ has\ been\ loaded.\n\n\ \ \ *\ reports\ in\ \$errorInfo\ are\ less\ meaningful\ since\ they\ reference\ the\ dynamically\ allocated\ namespaces.\ We\ mitigate\ this\ by\ providing\ 'loader\ errorInfo',\ which\ translates\ the\ namespace\ names\ into\ file\ names.\ However,\ Tk's\ background\ error\ reporting\ uses\ \$errorInfo.\n\ \ \ *\ you\ can't\ copy\ and\ paste\ procedures\ into\ wish/tclsh\ console,\ since\ procs\ no\ longer\ specify\ the\ namespace\ they\ belong\ to.\n\ \ \ *\ module\ authors\ must\ make\ rigorous\ use\ of\ \ 'namespace\ current'\ or\ 'namespace\ code'\ when\ defining\ \ callbacks.\n\nSee\ also\ \[An\ Alternative\ to\ Namespace\]\ for\ another\ approach\ to\ avoiding\ hardcoded\nnamespaces.\n\n**Implementation**\n\n'''Implementation'''\n\ #\ Uses\ 'namespace\ ensemble'.\n\ \n\ namespace\ eval\ ::Loader\ \{\}\ \{\n\ \ \ \ array\ set\ map\ \{\}\n\ \ \ \ variable\ nextId\ 0\n\ \}\n\ \n\ proc\ ::Loader::findFile\ \{clientNs\ fileName\}\ \{\n\ #\ Find\ the\ absolute\ pathname\ corresponding\ to\ \$fileName.\n\ \n\ \ \ \ if\ \{\[file\ pathtype\ \$fileName\]\ eq\ \"relative\"\}\ \{\n\ \ \ \ \n\ \ \ \ \ \ \ variable\ files\n\ \ \ \ \ \ \ if\ \{\[info\ exists\ files(\$clientNs)\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ set\ clientFile\ \$files(\$clientNs)\n\ \ \ \ \ \ \ \}\ else\ \{\n\ \ \ \ \ \ \ \ \ \ #\ Namespace\ was\ not\ allocated\ by\ Loader::use,\ just\ use\ the\ global\n\ \ \ \ \ \ \ \ \ \ #\ search\ path.\n\ \ \ \ \ \ \ \ \ \ set\ clientFile\ /\n\ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \ \ \ \ \ \n\ \ \ \ \ \ \ set\ child\ \$clientFile\n\ \ \ \ \ \ \ variable\ searchDirs\n\ \n\ \ \ \ \ \ \ while\ \{true\}\ \{\n\ \n\ \ \ \ \ \ \ \ \ \ foreach\ dir\ \[getSearchDirs\ \$child\]\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ if\ \{\[file\ exists\ \$dir/\$fileName\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ return\ \[file\ normalize\ \$dir/\$fileName\]\n\ \ \ \ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \ \ \}\n\ \n\ \ \ \ \ \ \ \ \ \ set\ parent\ \[file\ dirname\ \$child\]\n\ \ \ \ \ \ \ \ \ \ if\ \{\$parent\ eq\ \$child\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ break\;\n\ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \ \ \n\ \ \ \ \ \ \ \ \ \ set\ child\ \$parent\n\ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \n\ \ \ \ \ \ \ if\ \{\$child\ ne\ \"/\"\}\ \{\n\ \ \ \ \ \ \ \n\ \ \ \ \ \ \ \ \ \ #\ On\ Windows,\ need\ to\ consider\ /\ as\ a\ special\ case\ since\ file\ dirname\ C:\n\ \ \ \ \ \ \ \ \ \ #\ yields\ C:.\n\ \ \ \ \ \ \ \ \ \ foreach\ dir\ \[getSearchDirs\ /\]\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ if\ \{\[file\ exists\ \$dir/\$fileName\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ return\ \[file\ normalize\ \$dir/\$fileName\]\n\ \ \ \ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \ \ \n\ \ \ \ \ \ \ \ \ \ \n\ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \n\ \ \ \ \ \ \ error\ \"Cannot\ find\ file\ \$fileName\ in\ search\ path\ for\ \$clientFile\"\n\ \ \ \ \}\n\ \ \ \ \n\ \ \ \ return\ \[file\ normalize\ \$fileName\]\n\ \}\n\ \n\ proc\ ::Loader::getSearchDirs\ \{dir\}\ \{\n\ \ \ \ variable\ searchDirs\n\ \ \ \ set\ dirs\ \{\}\n\ \ \ \ catch\ \{set\ dirs\ \$searchDirs(\$dir)\}\n\ \ \ \ return\ \$dirs\n\ \}\n\ \n\ proc\ ::Loader::setSearchDirs\ \{dir\ dirs\}\ \{\n\ #\ Associate\ directory\ \$dir\ with\ list\ \$dirs.\n\ #\ When\ scripts\ located\ inside\ \$dir\ attempt\ to\ locate\n\ #\ a\ script\ via\ 'loader\ use',\ the\ directories\ in\ \$dirs\n\ #\ will\ be\ searched\ in\ the\ attempt\ to\ locate\ the\ script.\n\ \n\ \ \ \ variable\ searchDirs\n\ \ \ \ set\ searchDirs(\$dir)\ \$dirs\n\ \}\n\ \n\ proc\ ::Loader::addSearchDir\ \{markedDir\ searchDir\}\ \{\n\ #\ Adds\ a\ directory\ to\ the\ search\ path\ for\ \$dir.\n\ #\ This\ is\ a\ no-op\ if\ \$searchDir\ is\ already\ in\ the\ search\ path.\n\ \n\ \ \ \ #\ Hack\ for\ Windows,\ we\ treat\ '/'\ as\ the\ root\ directory,\ i.e.\n\ \ \ \ #\ the\ location\ for\ the\ root\ directory\ search\ path.\ However,\n\ \ \ \ #\ in\ windows,\ each\ drive\ letter\ is\ a\ root\ directory.\n\ \ \ \ if\ \{\$markedDir\ ne\ \"/\"\}\ \{\n\ \ \ \ \ \ \ set\ markedDir\ \[file\ normalize\ \$markedDir\]\n\ \ \ \ \}\n\ \ \ \ \n\ \ \ \ set\ searchDir\ \[file\ normalize\ \$searchDir\]\n\ \ \ \ set\ searchDirs\ \[getSearchDirs\ \$markedDir\]\n\ \ \ \ foreach\ dir\ \$searchDirs\ \{\n\ \ \ \ \ \ \ if\ \{\$dir\ eq\ \$searchDir\}\ \{\n\ \ \ \ \ \ \ \ \ \ return\n\ \ \ \ \ \ \ \}\n\ \ \ \ \}\n\ \ \ \ \n\ \ \ \ lappend\ searchDirs\ \$searchDir\n\ \ \ \ setSearchDirs\ \$markedDir\ \$searchDirs\n\ \}\n\ \n\ proc\ ::Loader::errorInfo\ \{\}\ \{\n\ #\ Returned\ a\ transformed\ \$errorInfo\ with\ dynamically\ allocated\n\ #\ namespaces\ replaced\ with\ the\ associated\ file\ names.\n\ \n\ \ \ \ variable\ files\n\ \ \ \ set\ result\ \"\"\n\ \ \ \ foreach\ line\ \[split\ \$::errorInfo\ \\n\]\ \{\n\ \ \ \ \ \ \ if\ \{\ \ \ \[regexp\ \{\ (\ *)\\(procedure\ \"(::dynamic\[0-9\]+)::(.*)\"\ line\ (\[0-9\]+)\\)\$\}\ \$line\ dummy\ space\ ns\ proc\ lineNum\]\n\ \ \ \ \ \ \ \ \ \ \ &&\ \[info\ exists\ files(\$ns)\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ \n\ \ \ \ \ \ \ \ \ \ append\ result\ \"\$\{space\}(procedure\ \\\"\$proc\\\"\ line\ \$lineNum\ -\ from\ file\ \$files(\$ns))\"\n\ \ \ \ \ \ \ \n\ \ \ \ \ \ \ \}\ else\ \{\n\ \ \ \ \ \ \ \ \ \ append\ result\ \$line\\n\n\ \ \ \ \ \ \ \}\n\ \ \ \ \}\n\ \ \ \ return\ \$result\n\ \}\n\ \n\ proc\ ::Loader::use\ \{args\}\ \{\n\ #\ Enables\ use\ of\ a\ module,\ loading\ it\ if\ necessary.\n\ #\ -import:\ \ imports\ all\ commands\ of\ the\ loaded\ module\ into\ the\ client's\ namespace\n\ #\ -cmd\ cmd:\ defines\ cmd\ as\ an\ ensemble\ through\ which\ the\ client\ can\n\ #\ \ \ \ \ \ \ \ \ \ \ access\ the\ commands\ of\ the\ loaded\ module\n\ #\ -force\ \ \ \ force\ loading\ of\ the\ module\ even\ if\ it\ was\ loaded\ previously.\n\ \n\ \ \ \ set\ extraArgs\ \{\}\n\ \ \ \ set\ mode\ static\n\ \ \ \ set\ argNum\ 0\n\ \ \ \ \n\ \ \ \ if\ \{\[llength\ \$args\]\ eq\ 0\}\ \{\n\ \ \ \ \ \ \ error\ \"wrong\ #\ args:\ should\ be\ \\\"loader\ use\\\"\ ?switches?\ fileName\\\"\"\n\ \ \ \ \}\n\ \ \ \ \n\ \ \ \ set\ fileName\ \[lindex\ \$args\ end\]\n\ \ \ \ \n\ \ \ \ set\ argNum\ 0\n\ \ \ \ set\ mode\ noop\n\ \ \ \ set\ force\ false\n\ \ \ \ while\ \{\$argNum\ <\ \[llength\ \$args\]-1\}\ \{\n\ \ \ \ \ \ \ set\ arg\ \[lindex\ \$args\ \$argNum\]\n\ \ \ \ \ \ \ switch\ --\ \$arg\ \{\n\ \ \ \ \ \ \ \n\ \ \ \ \ \ \ \ \ \ -cmd\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ set\ mode\ cmd\n\ \ \ \ \ \ \ \ \ \ \ \ \ incr\ argNum\n\ \ \ \ \ \ \ \ \ \ \ \ \ set\ cmd\ \[lindex\ \$args\ \$argNum\]\n\ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \ \ \n\ \ \ \ \ \ \ \ \ \ -import\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ set\ mode\ import\n\ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \ \ \n\ \ \ \ \ \ \ \ \ \ -force\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ set\ force\ true\n\ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \ \n\ \ \ \ \ \ \ \ \ \ default\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ lappend\ extraArgs\ \$arg\n\ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ incr\ argNum\n\ \ \ \ \}\n\ \ \ \ \n\ \ \ \ set\ clientNs\ \[uplevel\ 1\ namespace\ current\]\n\ \n\ \ \ \ set\ fileName\ \[findFile\ \$clientNs\ \$fileName\]\n\ \ \ \ variable\ nextId\n\ \ \ \ variable\ namespaces\n\ \ \ \ variable\ files\n\ \n\ \ \ \ set\ alreadyLoaded\ \[info\ exists\ namespaces(\$fileName)\]\n\ \ \ \ \n\ \ \ \ if\ \{\$alreadyLoaded\}\ \{\n\ \ \ \ \ \ \ set\ pkgNs\ \$namespaces(\$fileName)\n\ \ \ \ \}\ else\ \{\n\ \ \ \ \ \ \ set\ pkgNs\ ::dynamic\$nextId\n\ \ \ \ \ \ \ set\ namespaces(\$fileName)\ \$pkgNs\n\ \ \ \ \ \ \ set\ files(\$pkgNs)\ \$fileName\n\ \ \ \ \ \ \ incr\ nextId\n\ \ \ \ \}\n\ \n\ \ \ \ if\ \{!\$alreadyLoaded\ ||\ \$force\}\ \{\n\ \ \ \ \ \ \ #\ We\ knowthe\ directory\ contains\ at\ least\ one\ module\ so\ we\ make\ the\ directory\n\ \ \ \ \ \ \ #\ part\ of\ the\ search\ path\ for\ all\ scripts\ under\ the\ directory.\ This\ means\n\ \ \ \ \ \ \ #\ that\ all\ modules\ can\ load\ modules\ in\ the\ same\ directory\ using\ the\ leaf\ file\ name.\n\ \ \ \ \ \ \ set\ dir\ \[file\ dirname\ \$fileName\]\n\ \ \ \ \ \ \ addSearchDir\ \$dir\ \$dir\ \n\ \ \ \ \ \ \ \n\ \ \ \ \ \ \ uplevel\ #0\ namespace\ eval\ \$pkgNs\ \[list\ source\ \$extraArgs\ \$fileName\]\n\ \ \ \ \}\n\ \ \ \ \n\ \ \ \ switch\ --\ \$mode\ \{\n\ \ \ \ \ \ \ import\ \{\n\ \ \ \ \ \ \ \ \ \ namespace\ eval\ \$clientNs\ \[list\ namespace\ import\ ::\$\{pkgNs\}::*\]\n\ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \ \ namespace\ eval\ \$clientNs\ \{namespace\ import\ ::\$pkgNs::*\}\n\ \ \ \ \ \ \ cmd\ \{\n\ \ \ \ \ \ \ \ \ \ \n\ \ \ \ \ \ \ \ \ \ if\ \{!\[regexp\ \ ::\ \$cmd\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ #\ Normal\ case,\ client\ has\ specified\ the\ command\ within\ its\ namespace,\n\ \ \ \ \ \ \ \ \ \ \ \ \ #\ so\ concatenate\ them\ together.\n\ \ \ \ \ \ \ \ \ \ \ \ \ set\ cmd\ \$\{clientNs\}::\$cmd\n\ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \ \ #\ else\ client\ has\ specified\ the\ cmd\ name\ within\ the\ namespace\ hierarchy.\n\ \n\ \ \ \ \ \ \ \ \ \ namespace\ eval\ \$pkgNs\ \[list\ namespace\ ensemble\ create\ -command\ \$cmd\]\n\ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \n\ \ \ \ \ \ \ noop\ \{\n\ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \n\ \ \ \ \ \ \ default\ \{\n\ \ \ \ \ \ \ \ \ \ error\ \"Internal\ error\ -\ unknown\ mode\ \$mode\"\n\ \ \ \ \ \ \ \}\n\ \ \ \ \}\n\ \}\n\ \n\ namespace\ eval\ ::Loader\ \{\n\ \ \ \ namespace\ export\ \{\[a-z\]*\}\n\ \ \ \ namespace\ ensemble\ create\ -command\ ::loader\n\ \}\n\ \n\n'''Example\ usage'''\n\n###\ toplevel.tcl\n\n\ source\ Loader.tcl\n\ loader\ addSearchDir\ /\ \[pwd\]\n\ loader\ use\ -cmd\ m1\ SubDir/Module1.tcl\n\ \n\ m1\ a\n\ \n###\ SubDir/Module1.tcl\n\ \ loader\ use\ -cmd\ m2\ Module2.tcl\n\n\ proc\ a\ \{\}\ \{\n\ \ \ \ puts\ \"This\ is\ proc\ a\ from\ Module1\ defined\ in\ namespace\ \[namespace\ current\]\"\n\ \ \ \ m2\ a\n\ \}\n\ \n\ proc\ b\ \{\}\ \{\n\ \ \ \ puts\ \"This\ is\ proc\ b\ from\ Module1\ defined\ in\ namespace\ \[namespace\ current\]\"\n\ \n\ \}\n\ \n\ namespace\ export\ \{\[a-z\]*\}\n\n\n###\ SubDir/Module2.tcl\n\ \ \n\ #\ Note:\ Module1.tcl\ and\ Module2.tcl\ both\ use\ each\ other.\ This\ is\ not\n\ #\ necessarily\ desirable\ but\ illustrates\ the\ flexibility\ of\ the\ mechanism.\n\ loader\ use\ -cmd\ m1\ Module1.tcl\n\n\ loader\ use\ -cmd\ m2\ Module1.tcl\n\ \ \ \ puts\ \"This\ is\ proc\ a\ from\ Module2\ defined\ in\ namespace\ \[namespace\ current\]\"\n\ \ \ \ m1\ b\n\ \}\n\ \ \ \ m2\ b\n\ namespace\ export\ \{\[a-z\]*\}\n======\n regexp2} CALL {my render {Local Packages and Transparent Namespaces} \[SLB\]\ Small\ Tcl\ scripts\ are\ easy\ to\ write\ with\ short\ procedure\ names\nplaced\ in\ the\ root\ namespace.\ As\ scripts\ become\ larger,\ breaking\ them\ninto\ relatively\ self-contained\ components\ which\ can\ be\ re-used\ and\navoiding\ name\ conflicts\ between\ those\ components\ becomes\ important.\nThis\ is\ normally\ done\ with\ namespaces\ and\ packages.\n\nWhile\ the\ namespace\ and\ package\ mechanisms\ work\ reasonably\ well,\nthey\ do\ add\ clutter\ to\ scripts.\ \[http://www.tcl.tk/cgi-bin/tct/tip/189.html\]\nhas\ made\ it\ easier\ to\ write\ packages\ but\ you\ do\ still\ need\ to\ choose\na\ unique\ name\ for\ each\ package.\ Sometimes\ it\ just\ seems\ easier\nto\ do\ something\ like:\n\n\tsource\ \[file\ dirname\ \[info\ script\]\]/OtherScript.tcl\n\nand\ bypass\ the\ use\ of\ packages\ altogether.\n\nYet\ packages\ have\ benefits.\ This\ page\ \ explores\ an\ alternative\ that\ngives\ some\ of\ the\ benefits\ of\ packages\ and\ namespaces\ but\ with\ less\ clutter\nand\ reduced\ risk\ of\ name\ collisions...\n\nThe\ alternative\ is\ based\ on\ a\ 'loader'\ command\ which\ is\ used\ thus:\n\n\tloader\ use\ ?-force?\ ?-cmd\ cmd?\ ?-import?\ moduleName.tcl\n\nIts\ behaviour\ is\ somewhat\ similar\ to\ both\ 'source'\ and\ 'package\ require'.\n\nThe\ full\ pathname\ for\ moduleName.tcl\ is\ obtained\ (see\ later).\nIf\ the\ module\ has\ not\ already\ been\ loaded,\ the\ pathname\ is\ sourced.\ This\ is\ done\nwithin\ the\ context\ of\ a\ dynamically\ allocated\ namespace.\ Thus\ a\ procedure\ndefinition\ 'proc\ a\ \{\}\ \{\ ...\ \}'\ within\ moduleName.tcl\ will\ automatically\nbe\ distinguished\ from\ any\ other\ definition\ of\ 'proc\ a'.\n\nProvided\ the\ moduleName.tcl\ is\ sourced\ without\ error\ (or\ if\ it\ was\ previously\nloaded\ successfully),\ the\ commands\ it\ exports\ are\ made\ available\ to\ the\ client\nscript.\ This\ can\ be\ done\ directly\ by\ importing\ the\ commands\ into\ the\ client's\nnamespace\ (if\ the\ -import\ switch\ was\ specified)\ or\ by\ adding\ an\ ensemble\ncommand\ to\ the\ client's\ namespace\ that\ contains\ the\ exported\ commands.\n\nThe\ mechanism\ for\ determining\ the\ full\ pathname\ of\ a\ module\ loaded\ by\ 'loader\ use'\nis\ of\ course\ based\ on\ a\ search\ path\ of\ directories.\ However,\ it\ does\ not\ require\na\ single\ global\ search\ path.\ Instead,\ 'loader\ use'\ obtains\ the\ namespace\ from\nwhich\ it\ is\ called\ and\ uses\ that\ to\ determine\ the\ full\ pathname\ of\ the\ file\ which\ was\ sourced\nwhen\ the\ namespace\ was\ allocated.\ This\ pathname\ can\ have\ its\ own\ unique\ search\ path\nof\ directories.\ If\ the\ referenced\ module\ name\ is\ not\ found\ within\ the\ module's\nown\ search\ path,\ its\ parent\ directory\ is\ considered.\ The\ mechanism\ does\ not\nlook\ for\ modules\ in\ the\ parent\ directory\ directly,\ instead\ it\ checks\ whether\na\ search\ path\ has\ been\ defined\ for\ the\ directory.\ If\ it\ has\ that\ search\ path\ is\nused.\ The\ search\ then\ proceeds\ through\ all\ parent\ directories\ up\ to\ the\ top\ level.\n\nMost\ directories\ will\ have\ no\ associated\ search\ path,\ so\ use\ of\ a\ deep\ directory\nhierarchy\ will\ not\ trigger\ any\ additional\ file\ i/o.\ Some\ directories\ will\ specify\ntheir\ own\ pathname\ as\ the\ only\ entry\ in\ their\ search\ path.\ A\ few\ may\ have\ a\ search\ path\ncontaining\ other\ directories.\n\nEach\ time\ a\ module\ is\ loaded,\ the\ parent\ directory\ is\ added\ to\ its\ own\ search\ path.\nThis\ is\ a\ bit\ ad-hoc\ but\ seems\ useful.\n\nModules\ can\ be\ made\ globally\ available\ by\ placing\ their\ directory\ in\ the\ search\ path\nfor\ directory\ '/'.\ This\ requires\ a\ little\ special\ handing\ on\ Windows,\ where\npathnames\ such\ as\ 'C:/'\ are\ considered\ top\ level\ directories.\ That\ special\nhandling\ is\ built\ into\ the\ mechanism\ and\ so\ is\ transparent\ to\ its\ clients.\n\nThe\ -force\ switch\ allows\ a\ module\ to\ be\ reloaded.\ In\ such\ cases,\ it\ is\ reloaded\ninto\ the\ same\ namespace\ that\ was\ previously\ allocated\ for\ it.Commands\ that\ are\ not\ contained\ within\ a\ namespace\ allocated\ by\ 'loader\ use'\ncan\ load\ modules\ either\ by\ specifying\ the\ absolute\ path\ of\ the\ module\ or\ by\nreferencing\ modules\ accessible\ from\ the\ search\ path\ for\ directory\ '/'.\n\n\nThe\ result\ of\ all\ this\ is\ that\ you\ can\ create\ a\ collection\ of\ modules\ which\ reference\neach\ other\ using\ short\ descriptive\ names\ such\ as\ Process\ or\ Launcher.\ Such\ modules\ncan\ then\ be\ placed\ unchanged\ into\ a\ 'package'\ (though\ not\ a\ traditional\ Tcl\ package)\ncalled\ MyPackage.\ncan\ then\ be\ placed\ unchanged\ into\ a\ larger\ system\ in\ a\ package\ called\ MyPackage.\nbe\ turned\ into\ a\ package,\ and\ the\ original\ components\ might\ then\ be\ known\ to\nits\ clients\ as\ BigPackage/MyPackage/Process\ and\ BigPackage/MyPackage/Launcher.\nbe\ turned\ into\ a\ package,\ and\ the\ original\ components\ might\ then\ be\ known\ as\nBigPackage/MyPackage/Process\ and\ BigPackage/MyPackage/Launcher.\nversion\ checking.\ Of\ course\ a\ module\ can\ provide\ a\ version\ checking\ command\ that\ clients\nOne\ limitation\ of\ the\ mechanism\ compared\ to\ packages\ is\ that\ there's\ no\ support\ for\n\ncan\ call\ once\ the\ package\ has\ been\ loaded.\n\n\ \ \ *\ reports\ in\ \$errorInfo\ are\ less\ meaningful\ since\ they\ reference\ the\ dynamically\ allocated\ namespaces.\ We\ mitigate\ this\ by\ providing\ 'loader\ errorInfo',\ which\ translates\ the\ namespace\ names\ into\ file\ names.\ However,\ Tk's\ background\ error\ reporting\ uses\ \$errorInfo.\n\ \ \ *\ you\ can't\ copy\ and\ paste\ procedures\ into\ wish/tclsh\ console,\ since\ procs\ no\ longer\ specify\ the\ namespace\ they\ belong\ to.\n\ \ \ *\ module\ authors\ must\ make\ rigorous\ use\ of\ \ 'namespace\ current'\ or\ 'namespace\ code'\ when\ defining\ \ callbacks.\n\nSee\ also\ \[An\ Alternative\ to\ Namespace\]\ for\ another\ approach\ to\ avoiding\ hardcoded\nnamespaces.\n\n**Implementation**\n\n'''Implementation'''\n\ #\ Uses\ 'namespace\ ensemble'.\n\ \n\ namespace\ eval\ ::Loader\ \{\}\ \{\n\ \ \ \ array\ set\ map\ \{\}\n\ \ \ \ variable\ nextId\ 0\n\ \}\n\ \n\ proc\ ::Loader::findFile\ \{clientNs\ fileName\}\ \{\n\ #\ Find\ the\ absolute\ pathname\ corresponding\ to\ \$fileName.\n\ \n\ \ \ \ if\ \{\[file\ pathtype\ \$fileName\]\ eq\ \"relative\"\}\ \{\n\ \ \ \ \n\ \ \ \ \ \ \ variable\ files\n\ \ \ \ \ \ \ if\ \{\[info\ exists\ files(\$clientNs)\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ set\ clientFile\ \$files(\$clientNs)\n\ \ \ \ \ \ \ \}\ else\ \{\n\ \ \ \ \ \ \ \ \ \ #\ Namespace\ was\ not\ allocated\ by\ Loader::use,\ just\ use\ the\ global\n\ \ \ \ \ \ \ \ \ \ #\ search\ path.\n\ \ \ \ \ \ \ \ \ \ set\ clientFile\ /\n\ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \ \ \ \ \ \n\ \ \ \ \ \ \ set\ child\ \$clientFile\n\ \ \ \ \ \ \ variable\ searchDirs\n\ \n\ \ \ \ \ \ \ while\ \{true\}\ \{\n\ \n\ \ \ \ \ \ \ \ \ \ foreach\ dir\ \[getSearchDirs\ \$child\]\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ if\ \{\[file\ exists\ \$dir/\$fileName\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ return\ \[file\ normalize\ \$dir/\$fileName\]\n\ \ \ \ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \ \ \}\n\ \n\ \ \ \ \ \ \ \ \ \ set\ parent\ \[file\ dirname\ \$child\]\n\ \ \ \ \ \ \ \ \ \ if\ \{\$parent\ eq\ \$child\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ break\;\n\ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \ \ \n\ \ \ \ \ \ \ \ \ \ set\ child\ \$parent\n\ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \n\ \ \ \ \ \ \ if\ \{\$child\ ne\ \"/\"\}\ \{\n\ \ \ \ \ \ \ \n\ \ \ \ \ \ \ \ \ \ #\ On\ Windows,\ need\ to\ consider\ /\ as\ a\ special\ case\ since\ file\ dirname\ C:\n\ \ \ \ \ \ \ \ \ \ #\ yields\ C:.\n\ \ \ \ \ \ \ \ \ \ foreach\ dir\ \[getSearchDirs\ /\]\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ if\ \{\[file\ exists\ \$dir/\$fileName\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ return\ \[file\ normalize\ \$dir/\$fileName\]\n\ \ \ \ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \ \ \n\ \ \ \ \ \ \ \ \ \ \n\ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \n\ \ \ \ \ \ \ error\ \"Cannot\ find\ file\ \$fileName\ in\ search\ path\ for\ \$clientFile\"\n\ \ \ \ \}\n\ \ \ \ \n\ \ \ \ return\ \[file\ normalize\ \$fileName\]\n\ \}\n\ \n\ proc\ ::Loader::getSearchDirs\ \{dir\}\ \{\n\ \ \ \ variable\ searchDirs\n\ \ \ \ set\ dirs\ \{\}\n\ \ \ \ catch\ \{set\ dirs\ \$searchDirs(\$dir)\}\n\ \ \ \ return\ \$dirs\n\ \}\n\ \n\ proc\ ::Loader::setSearchDirs\ \{dir\ dirs\}\ \{\n\ #\ Associate\ directory\ \$dir\ with\ list\ \$dirs.\n\ #\ When\ scripts\ located\ inside\ \$dir\ attempt\ to\ locate\n\ #\ a\ script\ via\ 'loader\ use',\ the\ directories\ in\ \$dirs\n\ #\ will\ be\ searched\ in\ the\ attempt\ to\ locate\ the\ script.\n\ \n\ \ \ \ variable\ searchDirs\n\ \ \ \ set\ searchDirs(\$dir)\ \$dirs\n\ \}\n\ \n\ proc\ ::Loader::addSearchDir\ \{markedDir\ searchDir\}\ \{\n\ #\ Adds\ a\ directory\ to\ the\ search\ path\ for\ \$dir.\n\ #\ This\ is\ a\ no-op\ if\ \$searchDir\ is\ already\ in\ the\ search\ path.\n\ \n\ \ \ \ #\ Hack\ for\ Windows,\ we\ treat\ '/'\ as\ the\ root\ directory,\ i.e.\n\ \ \ \ #\ the\ location\ for\ the\ root\ directory\ search\ path.\ However,\n\ \ \ \ #\ in\ windows,\ each\ drive\ letter\ is\ a\ root\ directory.\n\ \ \ \ if\ \{\$markedDir\ ne\ \"/\"\}\ \{\n\ \ \ \ \ \ \ set\ markedDir\ \[file\ normalize\ \$markedDir\]\n\ \ \ \ \}\n\ \ \ \ \n\ \ \ \ set\ searchDir\ \[file\ normalize\ \$searchDir\]\n\ \ \ \ set\ searchDirs\ \[getSearchDirs\ \$markedDir\]\n\ \ \ \ foreach\ dir\ \$searchDirs\ \{\n\ \ \ \ \ \ \ if\ \{\$dir\ eq\ \$searchDir\}\ \{\n\ \ \ \ \ \ \ \ \ \ return\n\ \ \ \ \ \ \ \}\n\ \ \ \ \}\n\ \ \ \ \n\ \ \ \ lappend\ searchDirs\ \$searchDir\n\ \ \ \ setSearchDirs\ \$markedDir\ \$searchDirs\n\ \}\n\ \n\ proc\ ::Loader::errorInfo\ \{\}\ \{\n\ #\ Returned\ a\ transformed\ \$errorInfo\ with\ dynamically\ allocated\n\ #\ namespaces\ replaced\ with\ the\ associated\ file\ names.\n\ \n\ \ \ \ variable\ files\n\ \ \ \ set\ result\ \"\"\n\ \ \ \ foreach\ line\ \[split\ \$::errorInfo\ \\n\]\ \{\n\ \ \ \ \ \ \ if\ \{\ \ \ \[regexp\ \{\ (\ *)\\(procedure\ \"(::dynamic\[0-9\]+)::(.*)\"\ line\ (\[0-9\]+)\\)\$\}\ \$line\ dummy\ space\ ns\ proc\ lineNum\]\n\ \ \ \ \ \ \ \ \ \ \ &&\ \[info\ exists\ files(\$ns)\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ \n\ \ \ \ \ \ \ \ \ \ append\ result\ \"\$\{space\}(procedure\ \\\"\$proc\\\"\ line\ \$lineNum\ -\ from\ file\ \$files(\$ns))\"\n\ \ \ \ \ \ \ \n\ \ \ \ \ \ \ \}\ else\ \{\n\ \ \ \ \ \ \ \ \ \ append\ result\ \$line\\n\n\ \ \ \ \ \ \ \}\n\ \ \ \ \}\n\ \ \ \ return\ \$result\n\ \}\n\ \n\ proc\ ::Loader::use\ \{args\}\ \{\n\ #\ Enables\ use\ of\ a\ module,\ loading\ it\ if\ necessary.\n\ #\ -import:\ \ imports\ all\ commands\ of\ the\ loaded\ module\ into\ the\ client's\ namespace\n\ #\ -cmd\ cmd:\ defines\ cmd\ as\ an\ ensemble\ through\ which\ the\ client\ can\n\ #\ \ \ \ \ \ \ \ \ \ \ access\ the\ commands\ of\ the\ loaded\ module\n\ #\ -force\ \ \ \ force\ loading\ of\ the\ module\ even\ if\ it\ was\ loaded\ previously.\n\ \n\ \ \ \ set\ extraArgs\ \{\}\n\ \ \ \ set\ mode\ static\n\ \ \ \ set\ argNum\ 0\n\ \ \ \ \n\ \ \ \ if\ \{\[llength\ \$args\]\ eq\ 0\}\ \{\n\ \ \ \ \ \ \ error\ \"wrong\ #\ args:\ should\ be\ \\\"loader\ use\\\"\ ?switches?\ fileName\\\"\"\n\ \ \ \ \}\n\ \ \ \ \n\ \ \ \ set\ fileName\ \[lindex\ \$args\ end\]\n\ \ \ \ \n\ \ \ \ set\ argNum\ 0\n\ \ \ \ set\ mode\ noop\n\ \ \ \ set\ force\ false\n\ \ \ \ while\ \{\$argNum\ <\ \[llength\ \$args\]-1\}\ \{\n\ \ \ \ \ \ \ set\ arg\ \[lindex\ \$args\ \$argNum\]\n\ \ \ \ \ \ \ switch\ --\ \$arg\ \{\n\ \ \ \ \ \ \ \n\ \ \ \ \ \ \ \ \ \ -cmd\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ set\ mode\ cmd\n\ \ \ \ \ \ \ \ \ \ \ \ \ incr\ argNum\n\ \ \ \ \ \ \ \ \ \ \ \ \ set\ cmd\ \[lindex\ \$args\ \$argNum\]\n\ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \ \ \n\ \ \ \ \ \ \ \ \ \ -import\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ set\ mode\ import\n\ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \ \ \n\ \ \ \ \ \ \ \ \ \ -force\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ set\ force\ true\n\ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \ \n\ \ \ \ \ \ \ \ \ \ default\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ lappend\ extraArgs\ \$arg\n\ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ incr\ argNum\n\ \ \ \ \}\n\ \ \ \ \n\ \ \ \ set\ clientNs\ \[uplevel\ 1\ namespace\ current\]\n\ \n\ \ \ \ set\ fileName\ \[findFile\ \$clientNs\ \$fileName\]\n\ \ \ \ variable\ nextId\n\ \ \ \ variable\ namespaces\n\ \ \ \ variable\ files\n\ \n\ \ \ \ set\ alreadyLoaded\ \[info\ exists\ namespaces(\$fileName)\]\n\ \ \ \ \n\ \ \ \ if\ \{\$alreadyLoaded\}\ \{\n\ \ \ \ \ \ \ set\ pkgNs\ \$namespaces(\$fileName)\n\ \ \ \ \}\ else\ \{\n\ \ \ \ \ \ \ set\ pkgNs\ ::dynamic\$nextId\n\ \ \ \ \ \ \ set\ namespaces(\$fileName)\ \$pkgNs\n\ \ \ \ \ \ \ set\ files(\$pkgNs)\ \$fileName\n\ \ \ \ \ \ \ incr\ nextId\n\ \ \ \ \}\n\ \n\ \ \ \ if\ \{!\$alreadyLoaded\ ||\ \$force\}\ \{\n\ \ \ \ \ \ \ #\ We\ knowthe\ directory\ contains\ at\ least\ one\ module\ so\ we\ make\ the\ directory\n\ \ \ \ \ \ \ #\ part\ of\ the\ search\ path\ for\ all\ scripts\ under\ the\ directory.\ This\ means\n\ \ \ \ \ \ \ #\ that\ all\ modules\ can\ load\ modules\ in\ the\ same\ directory\ using\ the\ leaf\ file\ name.\n\ \ \ \ \ \ \ set\ dir\ \[file\ dirname\ \$fileName\]\n\ \ \ \ \ \ \ addSearchDir\ \$dir\ \$dir\ \n\ \ \ \ \ \ \ \n\ \ \ \ \ \ \ uplevel\ #0\ namespace\ eval\ \$pkgNs\ \[list\ source\ \$extraArgs\ \$fileName\]\n\ \ \ \ \}\n\ \ \ \ \n\ \ \ \ switch\ --\ \$mode\ \{\n\ \ \ \ \ \ \ import\ \{\n\ \ \ \ \ \ \ \ \ \ namespace\ eval\ \$clientNs\ \[list\ namespace\ import\ ::\$\{pkgNs\}::*\]\n\ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \ \ namespace\ eval\ \$clientNs\ \{namespace\ import\ ::\$pkgNs::*\}\n\ \ \ \ \ \ \ cmd\ \{\n\ \ \ \ \ \ \ \ \ \ \n\ \ \ \ \ \ \ \ \ \ if\ \{!\[regexp\ \ ::\ \$cmd\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ #\ Normal\ case,\ client\ has\ specified\ the\ command\ within\ its\ namespace,\n\ \ \ \ \ \ \ \ \ \ \ \ \ #\ so\ concatenate\ them\ together.\n\ \ \ \ \ \ \ \ \ \ \ \ \ set\ cmd\ \$\{clientNs\}::\$cmd\n\ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \ \ #\ else\ client\ has\ specified\ the\ cmd\ name\ within\ the\ namespace\ hierarchy.\n\ \n\ \ \ \ \ \ \ \ \ \ namespace\ eval\ \$pkgNs\ \[list\ namespace\ ensemble\ create\ -command\ \$cmd\]\n\ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \n\ \ \ \ \ \ \ noop\ \{\n\ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \n\ \ \ \ \ \ \ default\ \{\n\ \ \ \ \ \ \ \ \ \ error\ \"Internal\ error\ -\ unknown\ mode\ \$mode\"\n\ \ \ \ \ \ \ \}\n\ \ \ \ \}\n\ \}\n\ \n\ namespace\ eval\ ::Loader\ \{\n\ \ \ \ namespace\ export\ \{\[a-z\]*\}\n\ \ \ \ namespace\ ensemble\ create\ -command\ ::loader\n\ \}\n\ \n\n'''Example\ usage'''\n\n###\ toplevel.tcl\n\n\ source\ Loader.tcl\n\ loader\ addSearchDir\ /\ \[pwd\]\n\ loader\ use\ -cmd\ m1\ SubDir/Module1.tcl\n\ \n\ m1\ a\n\ \n###\ SubDir/Module1.tcl\n\ \ loader\ use\ -cmd\ m2\ Module2.tcl\n\n\ proc\ a\ \{\}\ \{\n\ \ \ \ puts\ \"This\ is\ proc\ a\ from\ Module1\ defined\ in\ namespace\ \[namespace\ current\]\"\n\ \ \ \ m2\ a\n\ \}\n\ \n\ proc\ b\ \{\}\ \{\n\ \ \ \ puts\ \"This\ is\ proc\ b\ from\ Module1\ defined\ in\ namespace\ \[namespace\ current\]\"\n\ \n\ \}\n\ \n\ namespace\ export\ \{\[a-z\]*\}\n\n\n###\ SubDir/Module2.tcl\n\ \ \n\ #\ Note:\ Module1.tcl\ and\ Module2.tcl\ both\ use\ each\ other.\ This\ is\ not\n\ #\ necessarily\ desirable\ but\ illustrates\ the\ flexibility\ of\ the\ mechanism.\n\ loader\ use\ -cmd\ m1\ Module1.tcl\n\n\ loader\ use\ -cmd\ m2\ Module1.tcl\n\ \ \ \ puts\ \"This\ is\ proc\ a\ from\ Module2\ defined\ in\ namespace\ \[namespace\ current\]\"\n\ \ \ \ m1\ b\n\ \}\n\ \ \ \ m2\ b\n\ namespace\ export\ \{\[a-z\]*\}\n======\n} CALL {my revision {Local Packages and Transparent Namespaces}} CALL {::oo::Obj280635 process revision/Local+Packages+and+Transparent+Namespaces} CALL {::oo::Obj280633 process}

-errorcode

NONE

-errorinfo

Unknow state transition: LINE -> END
    while executing
"error $msg"
    (class "::Wiki" method "render_wikit" line 6)
    invoked from within
"my render_$default_markup $N $C $mkup_rendering_engine"
    (class "::Wiki" method "render" line 8)
    invoked from within
"my render $name $C"
    (class "::Wiki" method "revision" line 31)
    invoked from within
"my revision $page"
    (class "::Wiki" method "process" line 56)
    invoked from within
"$server process [string trim $uri /]"

-errorline

4