Error processing request

Parameters

CONTENT_LENGTH0
REQUEST_METHODGET
REQUEST_URI/revision/Sugar+command+macros?V=19
QUERY_STRINGV=19
CONTENT_TYPE
DOCUMENT_URI/revision/Sugar+command+macros
DOCUMENT_ROOT/var/www/nikit/nikit/nginx/../docroot
SCGI1
SERVER_PROTOCOLHTTP/1.1
HTTPSon
REMOTE_ADDR172.70.130.243
REMOTE_PORT38310
SERVER_PORT4443
SERVER_NAMEwiki.tcl-lang.org
HTTP_HOSTwiki.tcl-lang.org
HTTP_CONNECTIONKeep-Alive
HTTP_ACCEPT_ENCODINGgzip, br
HTTP_X_FORWARDED_FOR3.128.94.171
HTTP_CF_RAY877069400e8b2c1d-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_IP3.128.94.171
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 {Sugar command macros} ***\ NOTE\ ***\n\ The\ API\ changed\ in\ two\ ways,\ you\ should\ be\ aware\ of\ this\ if\ you\ want\ to\ try\ with\ the\ current\ release\ of\n\ sugar\ what\ is\ shown\ in\ this\ tutorial.\n\n\ First\ Change\ -\ Now\ macros\ are\ expanded\ only\ inside\ procedures\ defined\ with\ ::sugar::proc\n\ Second\ Change\ -\ Now\ macros\ get\ a\ list\ of\ arguments\ like\ normal\ procedures,\ but\ the\ first\ argument\ is\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ the\ macro\ name\ itself.\ All\ the\ macros\ in\ this\ tutorial\ will\ work\ substituting\ the\ 'argv'\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ argument\ with\ 'args'.\n\n\ \ \ \ *\ Section\ 0\ -\ '''\[Sugar\]'''\n\ \ \ \ *\ Section\ 1\ -\ '''\[Sugar\ command\ macros\]'''\ (what\ you\ are\ reading)\n\ \ \ \ *\ Section\ 2\ -\ '''\[Sugar\ syntax\ macros\]'''\n\ \ \ \ *\ Section\ 3\ -\ '''\[Sugar\ transformers\]'''\n\n'''What\ is\ a\ Tcl\ macro'''.\n\nA\ macro\ is\ an\ operator\ implemented\ by\ transformation.\ Macros\ are\ procedures\nthat\ generate\ a\ Tcl\ program\ at\ compile\ time,\ and\ substitute\ it\ where\nthe\ programmer\ used\ thier\ name\ as\ command.\n\nIt's\ a\ simple\ concept\ if\ explained\ by\ examples.\n\nSuppose\ you\ want\ to\ write\ a\ \[\[clear\]\]\ command,\ that\ set\ the\ variable\nname\ passed\ as\ it's\ unique\ argument,\ to\ a\ null\ string.\ Implemented\nas\ a\ procedure\ using\ \[upvar\]\ it\ is\ like\ this:\n\n======\n\ proc\ clear\ varName\ \{\n\ \ \ \ \ upvar\ 1\ \$varName\ var\n\ \ \ \ \ set\ var\ \{\}\n\ \}\n======\n\nEvery\ time\ the\ user\ type\n\n======\n\ \ clear\ myvar\n======\n\nin\ a\ program,\ the\ \[\[clear\]\]\ procedure\ will\ be\ called,\ and\ the\ variable\nmyvar\ of\ the\ caller\ procedure\ will\ be\ set\ to\ a\ null\ string.\n\nAs\ an\ alternative\ to\ call\ a\ procedure\ that\ is\ able\ to\ alter\ the\ caller's\nexecution\ environment,\ we\ may\ want\ to\ automatically\ substitute\ every\ occurrence\nof\ the\ command\ \[\[clear\ <varname>\]\]\ with\ \[\[set\ <varname>\ \{\}\]\].\n\nSo\ basically\ we\ want\ that\ when\ we\ write\n\n======\n\ \ clearn\ myvar\n======\n\nin\ a\ program,\ it\ is\ substitute\ with\n\n======\n\ \ set\ myvar\ \{\}\n======\n\nas\ if\ the\ programmer\ had\ really\ typed\ \"set\ myvar\ \{\}\"\ instead\ of\ \"clear\ myvar\".\nThat's\ the\ goal\ of\ the\ simplest\ form\ of\ a\ \[Sugar\]'s\ macro.\n\nThe\ definition\ of\ a\ new\ macro\ is\ very\ similar\ to\ the\ creation\ of\ a\ procedure.\nThe\ following\ is\ the\ implementation\ of\ \[\[clear\]\]\ as\ a\ macro:\n\n======\n\ sugar::macro\ clear\ argv\ \{\n\ \ \ \ list\ set\ \[lindex\ \$argv\ 1\]\ \{\{\}\}\n\ \}\n======\n\nIt\ means:\ \"If\ you\ encounter\ a\ command\ called\ 'clear'\ inside\ the\ source\ code,\ncall\ the\ following\ procedure\ putting\ all\ the\ parts\ of\ which\ the\ncommand\ is\ composed\ in\ \$argv,\ and\ substitute\ the\ occurrence\ of\ the\ clear\ncommand\ and\ arguments,\ with\ what\ the\ procedure\ will\ return.\"\n\nAgain,\ with\ other\ words:\n\nSo,\ what\ happens\ is\ that\ when\ a\ procedure\ is\ compiled,\ for\ every\noccurrence\ of\ the\ \[\[clear\]\]\ command\ inside\ the\ procedure,\ the\ above\nprocedure\ is\ called,\ with\ \$argv\ set\ to\ a\ list\ that\ represents\ the\ arguments\nused\ to\ call\ the\ macro\ (including\ the\ macro\ name\ itself\ as\ first\ argument).\nThe\ result\ value\ of\ the\ function,\ that\ should\ be\ a\ list\ of\ the\ same\ form,\ is\nsubstituted\ in\ place\ of\ the\ original\ macro\ call.\n\nTo\ make\ the\ example\ more\ concrete,\ see\ the\ following\ code:\n\n======\n\ proc\ foobar\ \{\n\ \ \ \ \ set\ x\ 10\n\ \ \ \ \ clear\ x\n\ \}\n======\n\nBefore\ to\ compile\ the\ procedure,\ Tcl\ will\ call\ the\ macro\ we\ndefined\ with\ \$argv\ set\ to\ the\ two\ elements\ list\ \{clear\ x\}\ (verbatim).\nThat\ procedure\ returns\n\n======\n\ \ list\ set\ \[lindex\ \$argv\ 1\]\ \{\{\}\}\n======\n\nso\ for\ the\ argument\ \{clear\ x\}\ it\ will\ return\ the\ list\ \{set\ x\ \{\}\}.\nThis\ return\ value\ will\ be\ substituted\ in\ place\ of\ \"clear\ x\".\n\nActually,\ after\ the\ proc\ was\ defined,\ we\ can\ use\ \[\[info\ body\]\]\nto\ check\ what\ happened\ with\ the\ macro:\n\n======\n\ \ info\ body\ proc\n======\n\nwill\ output\n\n======\n\ \ \ \ \ set\ x\ 10\n\ \ \ \ \ set\ x\ \{\}\n======\n\nAt\ this\ point\ it's\ possible\ to\ use\ the\ \[\[clear\]\]\ macro\ as\ it\ was\ a\ Tcl\nprocedure.\n\nBut\ Tcl\ has\ \[\[uplevel\]\]\ and\ \[\[upvar\]\],\ so\ for\ what\ macros\nare\ useful?\ Fortuantely\ they\ allows\ for\ many\ interesting\ things\nnot\ possible\ at\ all\ otherwise,\ still,\ this\ example\nshows\ the\ first\ big\ advantage\ of\ macros:\n\n'''1)\ Macros\ makes\ Tcl\ faster,\ without\ to\ force\ the\ user\ to\ inline\ code\nby\ hand.'''\n\nThe\ \[\[clear\]\]\ command\ implemented\ as\ macro\ runs\ 3\ times\ faster\ in\ my\ Tcl\ 8.4.\n\nAlso,\ being\ \[\[upvar\]\]\ one\ of\ the\ biggest\ obstacles\ in\ the\ ability\nof\ the\ Tcl\ compiler\ to\ optimize\ Tcl\ bytecode,\ it's\ not\ impossible\nthat\ at\ some\ point\ Tcl\ will\ be\ able\ to\ run\ much\ faster\nif\ the\ user\ will\ ensure\ a\ given\ procedure\ is\ never\ target\ of\ \[\[upvar\]\].\n\nSimple\ commands\ that\ involve\ the\ use\ of\ upvar\ can\ be\ even\nmore\ simple\ to\ write\ implemented\ as\ macros.\ The\ following\ are\ four\ examples:\n\n======\n\ #\ \[first\ \$list\]\ -\ expands\ to\ \[lindex\ \$list\ 0\]\n\ sugar::macro\ first\ argv\ \{\n\ \ \ \ list\ lindex\ \[lindex\ \$argv\ 1\]\ 0\n\ \}\n\n\ #\ \[rest\ \$list\]\ -\ expands\ to\ \[lrange\ \$list\ 1\ end\]\n\ sugar::macro\ rest\ argv\ \{\n\ \ \ \ list\ lrange\ \[lindex\ \$argv\ 1\]\ 1\ end\n\ \}\n\n\ #\ \[last\ \$list\]\ -\ expands\ to\ \[lindex\ \$list\ end\]\n\ sugar::macro\ last\ argv\ \{\n\ \ \ \ list\ lindex\ \[lindex\ \$argv\ 1\]\ end\n\ \}\n\n\ #\ \[drop\ \$list\]\ -\ expands\ to\ \[lrange\ \$list\ 0\ end-1\]\n\ sugar::macro\ drop\ argv\ \{\n\ \ \ \ list\ lrange\ \[lindex\ \$argv\ 1\]\ 0\ end-1\n\ \}\n======\n\n\[Sugar\]\ supports\ three\ types\ of\ macros.\ We\ are\ dealing\ with\nthe\ simplest\ and\ more\ common\ macros:\ command\ macros.\n\nThe\ other\ two\ types,\ syntax\ macros,\ and\ transformers,\nwill\ be\ covered\ later.\ For\ now\ let's\ go\ to\ create\na\ more\ complex\ macro.\n\n'''A\ more\ complex\ example'''\n\nGood\ macros\ do\ source\ code\ transformation\ in\ a\ smart\ way,\nthey\ turn\ a\ form\ that\ is\ undestood\ by\ the\nprogrammer,\ to\ one\ that\ is\ also\ understood\ by\ the\ compiler,\nthat's\ hard\ to\ type\ and\ use\ in\ raw\ form\ without\ the\nmacro\ support,\ but\ optimal\ otherwise.\n\nIdeally\ a\ macro\ should\ expand\ to\ a\ single\ command\ call\n(possibly\ including\ many\ other\ nested),\ and\ should\ not\nexpand\ to\ code\ that\ magically\ creates\ variables\ at\nruntime\ to\ store\ intermediate\ results\ all\ the\ times\ it\ncan\ be\ avoided\ (because\ there\ may\ be\ collisions\ with\nvariables\ in\ the\ function,\ or\ created\ by\ other\ bad\ macros.\nBtw,\ in\ the\ TODO\ list\ of\ sugar\ there\ is\ a\ way\ to\ generate\nunique\ local\ variable\ names).\n\nIf\ the\ macro\ is\ well\ written,\ then\ the\ programmer\ can\ use\ it\ like\nany\ other\ command\ without\ to\ care\ much.\n\nWe\ will\ see\ a\ more\ real\ example\ of\ macro\ that\ implements\nan\ very\ efficient\ \[\[lpop\]\]\ operator.\ It\ accepts\ only\ one\nargument,\ the\ name\ of\ a\ variable,\ and\ returns\ the\ last\nelement\ of\ the\ list\ stored\ inside\ the\ given\ variable.\nAs\ side\ effect,\ \[\[lpop\]\]\ removes\ the\ last\ element\ from\ the\ list.\n(it's\ something\ like\ the\ complementar\ of\ \[lappend\]).\n\nA\ pure-Tcl\ implementation\ is\ the\ following:\n\n======\n\ proc\ lpop\ listVar\ \{\n\ \ \ \ upvar\ 1\ \$listVar\ list\n\ \ \ \ set\ res\ \[lindex\ \$list\ end\]\n\ \ \ \ set\ list\ \[lrange\ \$list\ 1\ end\]\n\ \ \ \ return\ \$res\n\ \}\n======\n\nThis\ version\ of\ lpop\ is\ really\ too\ slow,\ in\ fact\ when\n\[\[lrange\]\]\ is\ called,\ it\ creates\ a\ new\ list\ object\ even\nif\ the\ original\ one,\ stored\ in\ the\ \$list\ variable,\ is\ going\nto\ be\ freed\ and\ replaced\ by\ the\ copy.\ To\ modify\ the\ list\non-place\ is\ far\ better.\n\nThe\ \[\[lrange\]\]\ implementation\ is\ able\ to\ perform\ this\noptimization\ if\ the\ object\ in\ \"not\ shared\"\ (if\ you\ don't\nknow\ about\ this\ stuff\ try\ to\ read\ the\ Wiki\ page\ about\ the\n\[K\]\ operator\ before\ to\ continue)\n\nSo\ it's\ better\ to\ write\ the\ proc\ using\ the\ \[K\]\ operator.\nThe\ lrange\ line\ should\ be\ changed\ to\ this:\n\n======\n\ \ \ \ set\ list\ \[lrange\ \[K\ \$list\ \[set\ list\ \"\"\]\]\ 1\ end\]\n======\n\nWith\ K\ being:\n\n======\n\ proc\ K\ \{x\ y\}\ \{\n\ \ \ \ \ return\ \$x\n\ \}\n======\n\nBut\ even\ to\ call\ \[K\]\ is\ costly\ in\ terms\ of\ performace,\ so\nwhy\ don't\ inline\ it\ also?\ To\ do\ it\ requires\ to\ change\nthe\ previous\ lrange\ line\ to\ this:\n\n======\n\ \ \ \ set\ list\ \[lrange\ \[lindex\ \[list\ \$list\ \[set\ list\ \"\"\]\]\ 0\]\ 1\ end\]\n======\n\nThat's\ really\ a\ mess\ to\ read,\ but\ works\ at\ different\ speed,\ and\neven\ more\ important,\ at\ different\ time\ complexity!.\n\nWriting\ a\ macro\ for\ \[\[lpop\]\]\ we\ can\ go\ even\ faster,\ and,\ at\ the\nsame\ time,\ we\ can\ have\ this\ code\ more\ easy\ to\ maintain\ and\nread.\ Actually\ macros\ are\ allowed\ to\ expand\ to\ commands\ containing\nother\ macros,\ and\ so\ on.\ This\ means\ that\ we\ can\ write\ a\ macro\nfor\ every\ single\ step\ of\ \[\[lpop\]\].\ We\ need\ the\ \[\[first\]\]\n\[\[last\]\]\ and\ \[\[drop\]\]\ macros\ already\ developed,\ and\ a\ macro\ for\ \[K\]:\n\n======\n\ sugar::macro\ K\ argv\ \{\n\ \ \ \ foreach\ \{x\ y\}\ \$argv\ break\n\ \ \ \ list\ first\ \"\[list\ \$x\ \$y\]\"\n\ \}\n======\n\nNote\ that\ we\ used\ \[foreach\]\ instead\ of\ two\ calls\ to\ \[lindex\]\nthat's\ probably\ faster.\ But\ remember\ that\ macros\ don't\ have\nto\ be\ fast\ in\ the\ *generation*\ of\ the\ expanded\ code.\n\nThis\ will\ expand\ \[\[K\ \$x\ \$y\]\]\ into\ \[\[first\ \[\[list\ \$x\ \$y\]\]\]\],\ that\ will\nbe\ expanded\ in\ \[\[lindex\ \[\[list\ \$x\ \$y\]\]\ 0\]\].\n\nWe\ have\ one\ last\ problem.\ Even\ after\ the\ optimization\ and\ the\nuse\ of\ \[\[K\]\]\ inline,\ the\ procedure\ above\ required\ a\ local\nvariable\ 'res'\ to\ save\ the\ last\ argument\ of\ the\ list\ before\nto\ modify\ it,\ and\ use\ \$res\ later\ as\ return\ value\ for\ the\ procedure.\nWe\ don't\ want\ to\ create\ local\ vars\ into\ the\ code\ that\ calls\nthe\ \[\[lpop\]\]\ macro,\ nor\ we\ want\ to\ expand\ to\ more\ than\ a\ single\ncommand.\ The\ \[K\]\ operator\ can\ help\ us\ to\ do\ so.\ Instead\ to\nuse:\n\n======\n\ \ \ \ set\ res\ \[lindex\ \$list\ end\]\n\ \ \ \ set\ list\ \[lrange\ \[lindex\ \[list\ \$list\ \[set\ list\ \"\"\]\]\ 0\]\ 1\ end\]\n\ \ \ \ return\ \$res\n======\n\nwhy\ don't\ just\ write:\n\n======\n\ \ \ \ K\ \[lindex\ \$list\ end\]\ \[set\ list\ \[lrange\ \[lindex\ \[list\ \$list\ \[set\ list\ \"\"\]\]\ 0\]\ 1\ end\]\]\n======\n\nThat's\ ok,\ but\ what\ an\ unreadable\ code!\ Thanks\ to\ amcros\nwe\ can\ abstract\ from\ the\ fact\ that\ to\ call\ procedures\ is\nslow,\ so\ we\ just\ write:\n\n======\n\ \ \ \ \[K\ \[last\ \$list\]\ \[set\ list\ \[rest\ \[K\ \$list\ \[set\ list\ \"\"\]\]\]\]\]\n======\n\nWill\ not\ win\ the\ clean-code\ context\ this\ year,\ but\ it's\ much\nbetter\ than\ the\ previous.\ Ok...\ now\ we\ want\ a\ macro\ that,\ every\ time\ we\ntype\ \"lpop\ \$list\",\ will\ expand\ in\ the\ above\ line:\n\n======\n\ sugar::macro\ lpop\ argv\ \{\n\ \ \ \ set\ varname\ \[lindex\ \$argv\ 1\]\n\ \ \ \ set\ argv\ \[list\ \\\n\ \ \ \ \ \ \ \ K\ \\\n\ \ \ \ \ \ \ \ \{\[last\ \$%varname%\]\}\ \\\n\ \ \ \ \ \ \ \ \{\[set\ list\ \[drop\ \[K\ \$%varname%\ \[set\ %varname%\ \"\"\]\]\]\]\}\ \\\n\ \ \ \ \]\n\ \ \ \ foreach\ i\ \{1\ 2\}\ \{\n\ \ \ \ \ \ \ \ lset\ argv\ \$i\ \[string\ map\ \[list\ %varname%\ \$varname\]\ \[lindex\ \$argv\ \$i\]\]\n\ \ \ \ \}\n\ \ \ \ return\ \$argv\n\ \}\n======\n\[MDR\]:\ Why\ not\ just:\n\ sugar::macro\ lpop\ argv\ \{\n\ \ \ \ string\ map\ \[list\ %varname%\ \[lindex\ \$argv\ 1\]\]\ \{K\ \{\[last\ \$%varname%\]\}\ \{\[set\ list\ \[drop\ \[K\ \$%varname%\ \[set\ %varname%\ \"\"\]\]\]\]\}\}\n\ \}\n======\n\nThere\ are\ few\ things\ to\ note\ about\ this\ code.\ The\ macro\ returns\na\ list,\ where\ every\ element\ is\ a\ token\ of\ a\ Tcl\ command\nin\ the\ source\ code.\ This\ does\ not\ mean\ we\ have\ to\ transform\ in\nlists\ even\ arguments\ that\ happens\ to\ represent\ a\ script.\ Also\nnote\ that\ the\ input\ list\ of\ the\ macro\ is\ just\ a\ list\ of\ tokens\nthat\ are\ *exactly*\ what\ the\ user\ typed\ they\ in\ the\ source\ code,\ verbatim.\nWhat\ follows\ is\ that\ the\ tokens\ are\ already\ quoted\ and\ valid\nrepresentations\ of\ a\ procedure\ argument.\nWe\ don't\ need\ to\ care\ about\ the\ fact\ that\ they\ must\ be\ interpreted\nas\ a\ single\ argument\ like\ if\ we\ were\ generating\ code\ to\ pass\ to\ \[eval\].\n\nThis\ allows\ the\ macro\ developer\ to\ use\ templates\ for\ macros,\ in\nfact\ the\ \[\[lpop\]\]\ macro\ is\ just\ using\ a\ three\ argument\ template,\nand\ the\ final\ foreach\ will\ substitute\ the\ arguments\ that\ needs\nto\ refer\ to\ the\ variable\ name,\ with\ that\ name.\ You\ don't\ have\nto\ care\ what\ that\ variable\ name\ is.\ It\ can\ be\ a\ complex\ string\nformed\ by\ more\ commands,\ vars,\ and\ so\ on\ \[\[like\]\]\[\[this\]\]\$and-this.\nIf\ it\ was\ a\ single\ argument\ in\ the\ source\ code,\ it\ will\ be\ in\ the\ macro\nafter\ the\ expansion.\n\nAnother\ interesting\ thing\ to\ note\ is\ that\ we\ don't\ really\ have\nto\ return\ every\ token\ as\ a\ different\ element\ of\ the\ list.\ In\ pratice\nwe\ can\ return\ it\ even\ as\ a\ single-element\ list.\nThe\ rule\ is\ that\ the\ macro\ expander\ will\ care\ to\ put\ an\ argument\nseparator\ like\ a\ tab,\ or\ a\ space,\ for\ every\ element\ of\ the\nlist,\ and\ put\ a\ command\ separator\ like\ newline\ or\ \"\;\"\ at\ the\ end.\nIf\ we\ put\ spaces\ ourself,\ we\ can\ just\ return\ a\ single\ element\ list.\n\nSo,\ the\ lpop\ macro\ can\ also\ by\ written\ in\ this\ way:\n\n======\n\ sugar::macro\ lpop\ argv\ \{\n\ \ \ \ set\ varname\ \[lindex\ \$argv\ 1\]\n\ \ \ \ set\ cmd\ \[format\ \{\n\ \ \ \ \ \ \ \ K\ \[last\ \$%varname%\]\ \[set\ list\ \[drop\ \[K\ \$%varname%\ \[set\ %varname%\ \"\"\]\]\]\]\n\ \ \ \ \}\ \$varname\ \$varname\ \$varname\]\n\ \ \ \ return\ \[list\ \$cmd\]\n\ \}\n======\n\nThis\ is\ much\ more\ simple\ and\ clean,\ and\ actually\ it's\ possible\ to\nuse\ this\ style.\ The\ difference\ is\ that\ returning\ every\ token\ as\na\ different\ element\ of\ a\ list\ makes\ \[Sugar\]\ macros\ able\ to\nleft\ the\ indentation\ of\ the\ original\ code\ unaltered.\ This\ is\ helpful\nboth\ to\ take\ procedure\ error's\ line\ numbers\ correct,\ and\ to\nsee\ a\ good\ locking\ output\ of\ \[info\ body\].\ But\ as\ long\ as\nmost\ macros\ are\ about\ commands\ that\ are\ just\ typed\ in\ the\ same\ line\ntogether\ with\ all\ the\ arguments,\ for\ many\ macros\ is\ just\ a\ matter\ of\ tastes.\n\nIf\ you\ are\ implementing\ control\ structures\ that\ are:\n\n======\n\ indented\ \{in\}\ \{\n\ \ \ \ this\ way\n\ \}\n======\n\nIt's\ another\ question,\ and\ it's\ better\ to\ return\ every\ token\ as\ a\nlist\ element.\n\n'''Number\ of\ argument\ and\ other\ static\ checks\ in\ macros'''\n\nMacros\ expand\ to\ code\ that\ will\ raise\ an\ error\ if\ the\ number\ of\narguents\ is\ wrong\ in\ most\ cases,\ but\ it's\ possible\ to\ add\ this\ncontrol\ inside\ the\ macro.\ Actually\ it's\ a\ big\ advantage\ of\ macros\nbecause\ they\ are\ able\ to\ signal\ a\ bad\ number\ of\ arguments\ at\nrun\ time:\ this\ can\ help\ to\ write\ applications\ that\ are\ more\ reliable.\nIt's\ even\ possible\ to\ write\ a\ macro\ that\ expands\ to\ exactly\ what\nthe\ user\ typed\ in,\ but\ as\ side\ effect\ does\ a\ static\ check\ for\nbad\ number\ (or\ format)\ of\ arguments:\n\n======\n\ sugar::macro\ set\ argv\ \{\n\ \ \ \ if\ \{\[llength\ \$argv\]\ !=\ 3\ ||\ \[llength\ \$argv\]\ !=\ 2\}\ \{\n\ \ \ \ \ \ \ \ error\ \"Bad\ number\ of\ arguments\ for\ set\"\n\ \ \ \ \}\n\ \ \ \ return\ \$argv\n\ \}\n======\n\nThis\ macro\ returns\ \$argv\ itself,\ so\ it's\ an\ identity\ transformation,\nbut\ will\ raise\ errors\ for\ \[\[set\]\]\ with\ a\ bad\ number\ of\narguments\ even\ for\ code\ that\ will\ never\ be\ reached\nin\ the\ application.\ Note\ that\ the\ previous\ macro\ for\ set\ is\ a\ bit\nincomplete:\ to\ get\ it\ right\ we\ should\ add\ checks\ for\ arguments\nthat\ starts\ with\ \[\{*\}\],\ for\ this\ reason\ \[Sugar\]\ will\ provide\ a\ function\nto\ automatically\ search\ for\ a\ bad\ number\ of\ arguments\ in\ some\nnext\ version.\n\nNote\ that\ \{*\}\ introduces\ for\ the\ first\ time\ the\ possibility\ for\na\ command\ to\ get\ a\ number\ of\ arguments\ that\ is\ non\ evident\ reading\nthe\ source\ code\ but\ computed\ at\ runtime.\ Actually\ \{*\}\ is\ an\nadvantage\ for\ static\ checks\ because\ before\ of\ it\ the\ way\ to\ngo\ was\ \[\[eval\]\],\ that\ does\ totally\ \"hide\"\ the\ called\ command\ postponing\nall\ the\ work\ at\ run-time.\ With\ \{*\}\ it's\ always\ possible\nto\ say\ from\ the\ source\ code\ that\ a\ command\ is\ called\ with\ *at\ least*\ N\narguments.\ Still,\ to\ add\ new\ syntax\ to\ Tcl\ will\ probably\ not\ play\nwell\ with\ macros\ and\ other\ form\ of\ source\ code\ processing.\n\nIdentity\ macros\ are\ very\ powerful\ to\ perform\ static\ syntax\ checks,\nthey\ can\ not\ only\ warn\ on\ bad\ number\ of\ arguments,\ but\ with\ the\ntype\ of\ this\ arguments.\ See\ for\ example\ the\ following\ identity\nmacro\ for\ \"string\ is\":\n\n======\n\ proc\ valid_string_class\ class\ \{\n\ \ \ \ set\ classes\ \{alnum\ alpha\ ascii\ control\ boolean\ digit\ double\ false\ graph\ integer\ lower\ print\ punct\ space\ true\ upper\ wordchar\ xdigit\}\n\ \ \ \ set\ first\ \[string\ index\ \$class\ 0\]\n\ \ \ \ if\ \{\$first\ eq\ \{\$\}\}\ \{return\ 1\}\n\ \ \ \ if\ \{\$first\ eq\ \{\[\}\}\ \{return\ 1\}\n\ \ \ \ if\ \{\[lsearch\ \$classes\ \$class\]\ !=\ -1\}\ \{return\ 1\}\n\ \ \ \ return\ 0\n\ \}\n\n\ sugar::macro\ string\ argv\ \{\n\ \ \ \ if\ \{\[lindex\ \$argv\ 1\]\ eq\ \{is\}\ &&\ \[llength\ \$argv\]\ >\ 2\}\ \{\n\ \ \ \ \ \ \ \ if\ \{!\[valid_string_class\ \[lindex\ \$argv\ 2\]\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ puts\ stderr\ \"Warning:\ invalid\ string\ class\ in\ procedure\ \[sugar::currentProcName\]\"\n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \}\n\ \ \ \ return\ \$argv\n\ \}\n======\n\nThanks\ to\ this\ macro\ it's\ possible\ to\ ensure\ that\ errors\ like\ to\ write\n\[\[string\ is\ number\]\]\ instead\ \[\[string\ is\ integer\]\]\ are\ discovered\ at\ncompile-time.\ In\ this\ respect\ macros\ can\ be\ seen\ as\ a\ programmable\nstatic\ syntax\ checker\ for\ Tcl.\ We\ will\ see\ how\ \"syntax\ macros\"\ are\neven\ more\ useful\ in\ this\ respect.\ This\ is\ the\ second\ feature\ that\nmacros\ add\ to\ Tcl:\n\n'''2)\ Macros\ are\ a\ powerful\ programmable\ static\ checker\ for\ Tcl\ scripts.'''\n\nActually\ I\ think\ it's\ worth\ to\ use\ macros\ even\ only\ for\ this\ during\nthe\ development\ process,\ and\ than\ flush\ they\ away.\n\n'''Conditional\ compilation'''\n\nThat's\ small\ and\ neat:\ we\ can\ write\ a\ simple\ macro\ that\ expands\ to\nsome\ code\ only\ if\ a\ global\ variable\ is\ set\ to\ non-zero.\ Let's\nwrite\ this\ macro\ that\ we\ call\ \[\[debug\]\].\n\n======\n\ sugar::macro\ debug\ argv\ \{\n\ \ \ \ if\ \{\$::debug_mode\}\ \{\n\ \ \ \ \ \ \ \ list\ if\ 1\ \[lindex\ \$argv\ 1\]\n\ \ \ \ \}\ else\ \{\n\ \ \ \ \ \ \ \ list\n\ \ \ \ \}\n\ \}\n======\n\nThan\ you\ can\ use\ it\ in\ your\ application\ like\ if\ it\ was\ a\ conditional:\n\n======\n\ #\ Your\ application\ ...\n\ debug\ \{\n\ \ \ \ set\ c\ 0\n\ \}\n\ while\ 1\ \{\n\ \ \ \ debug\ \{\n\ \ \ \ \ \ \ \ incr\ c\n\ \ \ \ \ \ \ \ if\ \{\$c\ >\ 100\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ error\ \"Too\ many\ iteractions...\"\n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \}\n\ \ \ \ ....\ do\ something\ ....\n\ \}\n======\n\nAll\ the\ \[\[debug\ \{someting\}\]\]\ commands\ are\ compiled\ as\ \[\[if\ 1\ \{something\}\]\]\nif\ the\ ::debug_mode\ variable\ is\ true.\ Instead\ if\ this\ var\ is\ false,\nthey\ will\ not\ be\ compiled\ at\ all.\n\nThat's\ the\ simplest\ example,\ you\ can\ write\ similar\ macros\ like\ \[\[ifunix\]\],\n\[\[ifwindows\]\],\ \[\[ifmac\]\],\ or\ even\ to\ expand\ to\ different\ procedures\ call\nif\ a\ given\ command\ is\ called\ with\ 2,\ 3\ or\ 4\ arguments.\ The\ limit\ is\nthe\ immagination.\n\n'''New\ control\ stuctures'''\n\nNot\ all\ the\ programming\ languages\ allow\ to\ write\ new\ control\ structures.\nTcl\ is\ one\ of\ this\ better\ languages\ that\ don't\ put\ the\ programmer\ninside\ a\ jail,\ but,\ not\ all\ the\ programming\ languages\ that\ allows\nto\ write\ new\ control\ structures,\ are\ able\ to\ make\ they\ efficient.\n\nTcl\ macros\ can\ make\ new\ control\ sturctures\ as\ fast\ as\ byte\ compiled\ncontrol\ structures,\ because\ user\ defined\ ones\ are\ usually\ syntax\ glue\nfor\ code\ transformations.\ Being\ macro\ transformers\nthat\ translates\ a\ from\ to\ another,\ that's\ a\ good\ fit\ for\ macros.\n\nThat's\ a\ macro\ for\ the\ ?:\ operator.\n\n======\n\ #\ ?:\ expands\n\ #\ \ \ ?:\ cond\ val1\ val2\n\ #\ to\n\ #\ \ \ if\ \$cond\ \{format\ val1\}\ \{format\ val2\}\n\ sugar::macro\ ?:\ argv\ \{\n\ \ \ \ if\ \{\[llength\ \$argv\]\ !=\ 4\}\ \{\n\ \ \ \ \ \ \ \ error\ \"Wrong\ number\ of\ arguments\"\n\ \ \ \ \}\n\ \ \ \ foreach\ \{_\ cond\ val1\ val2\}\ \$argv\ break\n\ \ \ \ list\ if\ \$cond\ \[list\ \[list\ format\ \$val1\]\]\ \[list\ \[list\ format\ \$val2\]\]\n\ \}\n======\n\nThe\ macro's\ comment\ shows\ the\ expansion\ performed.\nBeing\ it\ translated\ to\ an\ \[if\]\ command,\ it's\ as\ fast\ as\ a\nTcl\ builtin.\n\n'''How\ macros\ knows\ what's\ a\ script?'''\n\nI\ Tcl\ there\ are\ no\ types,\ nor\ special\ syntaxes\ for\ what\ is\ code\nand\ what\ is\ just\ a\ string,\ so\ you\ may\ wonder\ why\ macros\ are\nnot\ expanded\ in\ the\ following\ code:\n\n======\n\ puts\ \{\n\ \ \ \ set\ foo\ \{1\ 2\ 3\}\;\ \[first\ \$foo\]\n\ \}\n======\n\nBut\ they\ are\ expanded\ in\ this:\n\n======\n\ while\ 1\ \{\n\ \ \ \ set\ foo\ \{1\ 2\ 3\}\;\ \[first\ \$foo\]\n\ \}\n======\n\nI\ guess\ this\ is\ one\ of\ the\ main\ problems\ developers\ face\ designing\ a\nmacro\ system\ for\ Tcl,\ and\ even\ one\ of\ the\ better\ helpers\ of\ the\ idea\nthat\ a\ good\ macro\ system\ for\ Tcl\ is\ impossible\ because\ you\ can't\nsay\ what\ is\ code\ and\ what\ isn't.\n\n\[Sugar\]\ was\ designed\ to\ address\ this\ problem\ in\ the\ simplest\ possible\nof\ the\ ways:\ because\ it\ can't\ say\ if\ an\ argument\ is\ a\ script\ or\ not,\nmacro\ expansion\ is\ not\ performed\ in\ arguments,\ so\ in\ theory\ \[Sugar\]\nwill\ not\ expand\ the\ code\ that's\ argument\ to\ \[puts\],\ nor\ \[while\].\n\nBut\ of\ course,\ in\ the\ real\ world\ for\ a\ macro\ system\ to\ be\ usable,\nmacros\ should\ be\ expanded\ inside\ the\ \[while\],\ and\ not\ expanded\ in\ \[puts\],\nso\ the\ idea\ is\ that\ for\ commands\ you\ know\ the\ argument\ is\ a\ script,\nyou\ write\ a\ macro\ that\ returns\ the\ same\ command\ but\ with\nscript\ arguments\ macro-expanded.\ It\ is\ very\ simple\ and\ in\ pratice\nthis\ works\ well.\ For\ example\ that's\ the\ macro\ for\ \[while\]:\n\n======\n\ sugar::macro\ while\ argv\ \{\n\ \ \ \ lset\ argv\ 1\ \[sugar::expandExprToken\ \[lindex\ \$argv\ 1\]\]\n\ \ \ \ lset\ argv\ 2\ \[sugar::expandScriptToken\ \[lindex\ \$argv\ 2\]\]\n\ \}\n======\n\nThat's\ the\ macro\ for\ \[if\]:\n\n======\n\ sugar::macro\ if\ argv\ \{\n\ \ \ \ lappend\ newargv\ \[lindex\ \$argv\ 0\]\n\ \ \ \ lappend\ newargv\ \[sugar::expandExprToken\ \[lindex\ \$argv\ 1\]\]\n\ \ \ \ set\ argv\ \[lrange\ \$argv\ 2\ end\]\n\ \ \ \ foreach\ a\ \$argv\ \{\n\ \ \ \ \ \ \ \ switch\ --\ \$a\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ else\ -\ elseif\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ lappend\ newargv\ \$a\n\ \ \ \ \ \ \ \ \ \ \ \ \}\ \n\ \ \ \ \ \ \ \ \ \ \ \ default\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ lappend\ newargv\ \[sugar::expandScriptToken\ \$a\]\n\ \ \ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \}\n\ \ \ \ return\ \$newargv\n\ \}\n======\n\nAs\ you\ can\ see\ \[Sugar\]\ exports\ an\ API\ to\ perform\ expansion\ in\nTcl\ scripts\ and\ Expr\ expressions.\ There\ are\ similar\ macros\ for\n\[switch\],\ \[for\],\ and\ so\ on.\ If\ you\ write\ a\ new\ conditional\ or\nloop\ command\ with\ macros,\ you\ don't\ need\ it\ at\ all\ because\nthe\ macro\ will\ translate\ to\ code\ that\ contains\ some\ form\ of\na\ well\ known\ built-in\ conditional\ or\ loop\ command,\ and\ we\ already\nhave\ macros\ for\ this\ (remember\ that\ macros\ can\ return\ code\ with\nmacros).\n\nIf\ you\ write\ any\ other\ command\ that\ accept\ as\ arguments\ a\ Tcl\ script\nor\ expr\ expression,\ just\ write\ a\ little\ macro\ for\ it\ to\ do\nmacro\ expansion.\ This\ has\ a\ nice\ side\ effect:\n\n======\n\ proc\ nomacro\ script\ \{\n\ \ \ \ uplevel\ 1\ \$script\n\ \}\n======\n\nDon't\ write\ a\ macro\ for\ nomacro,\ and\ you\ have\ a\ ready-to-use\ncommand\ that\ works\ as\ a\ barrier\ for\ macro\ expansion.\n\nContinue\ with\ section\ 2\ -\ '''\[Sugar\ syntax\ macros\]'''\n\n----\n\n\[WHD\]:\ This\ is\ very\ cool,\ but\ I\ have\ to\ ask--why\ not\ allow\ macros\ to\nhave\ a\ standard\ Tcl\ argument\ list?\ \ That\ is,\ \n\n======\n\ sugar::macro\ mymacro\ \{args\}\ \{...\}\n======\n\nGives\ the\ behavior\ you\ describe\ here,\ while\n\n======\n\ sugar::macro\ mymacro\ \{a\ b\ c\}\ \{...\}\n======\n\nexplicitly\ creates\ a\ macro\ that\ takes\ three\ arguments\ and\ will\ngenerate\ a\ standard\ error\ message\ if\ you\ supply\ some\ other\ number?\n\n----\n\n\[SS\]:\ This\ can\ be\ a\ good\ idea,\ being\ always\ possible\ to\nuse\ 'args'\ as\ only\ argument\ to\ have\ the\ current\ behaviour.\ I\ used\na\ single\ list\ as\ input\ mainly\ because\ the\ same\ macro\ can\ have\ more\nthen\ a\ name,\ and\ in\ order\ to\ have\ the\ same\ interface\ for\ both\ncommand\ macros\ and\ syntax\ macros.\ For\ example:\n\n======\n\ sugar::macro\ \{*\ +\ -\ /\}\ argv\ \{\n\ \ \ \ list\ expr\ \[list\ \[join\ \[lrange\ \$argv\ 1\ end\]\ \"\ \[lindex\ \$argv\ 0\]\ \"\]\]\n\ \}\n======\n\nWill\ handle\ *\ +\ -\ /\ with\ the\ same\ code.\ Macros\ with\ more\ than\ a\ name\nmay\ in\ extreme\ cases\ even\ give\ different\ meanings\ for\ arguments\ in\nthe\ same\ position.\ Btw\ there\ is\ 'args'\ for\ this\ case.\ So\ I\ can\ change\nthe\ API\ to\ something\ like\ this:\n\n======\n\ \ sugar::macro\ \{name\ arg1\ arg2\ ...\}\ \{...\}\n======\n\nThat's\ like\ a\ Tcl\ proc,\ but\ with\ the\ name\ that\ was\ used\ to\ call\ the\ macro\nas\ the\ first\ argument.\ For\ syntax\ macros\ this\ format\ actually\ may\ not\ have\ a\nlot\ of\ sense,\ but\ there\ is\ still\ 'args'.\ I'll\ include\ this\ chage\ in\ the\nnext\ version\ if\ I'll\ not\ receive\ feedbacks\ against\ it.\ Thanks\ for\ the\nfeedback\ WHD.\n\n\[WHD\]:\ I\ think\ that\ on\ the\ whole\ I\ prefer\ the\ previous\ syntax\ for\ command\nmacros\;\ the\ macro\ can\ always\ have\ an\ implicit\ argument\ that\ is\ the\nmacro\ name.\ \ For\ example,\n\n======\n\ #\ Identity\ macro\n\ sugar::macro\ \{+\ -\ *\ /\}\ \{args\}\ \{\ return\ \"\$macroname\ \$args\"\ \}\n======\n\n\[JMN\]:\ I'd\ just\ like\ to\ add\ my\ vote\ for\ removing\ the\ macroname\ as\ first\ argument\ syntax.\nFrom\ my\ hacking\ about,\ it\ seems\ easy\ to\ make\ it\ implicitly\ available\ more\ or\ less\ as\ WHD\ suggests.\n(I\ don't\ *think*\ I\ broke\ anything..\ )\n----\n\n\[SS\]:\ For\ a\ different\ question\ about\ the\ sugar\ API,\ I\ wonder\ if\ Tclers\ninterested\ in\ this\ macro\ system\ feel\ better\ the\ current\ redefinition\nof\ \[proc\],\ or\ if\ it's\ better\ to\ provide\ a\ sugar::proc\ procedure\ that's\nexactly\ like\ \[proc\]\ but\ with\ macro\ expansion.\n\nIf\ the\ API\ will\ remain\ the\ current\ with\ \[proc\]\ redefined,\ I'll\ add\nin\ the\ wrapper\ an\ option\ -nomacro\ that\ will\ just\ call\ the\ original\ proc.\nPlease\ add\ your\ name\ with\ optional\ motivation\ below.\n\nYes,\ I\ think\ it's\ better\ to\ wrapper\ the\ real\ \[proc\]:\n\ \ \ \ *\ Put\ your\ name\ here\ if\ you\ are\ for\ this\ solution.\n\nNo,\ I\ want\ macro\ expansion\ only\ using\ sugar::proc:\n\ \ \ \ *\ \[SS\]\ (avoid\ to\ waste\ CPU\ time\ for\ procs\ that\ don't\ use\ macros,\ this\ can\ be\ a\ big\ difference\ if\ you\ \[package\ require\]\ sugar\ before\ Tk\ or\ other\ big\ packages)\n\ \ \ *\ \[DKF\]:\ Avoiding\ overriding\ the\ real\ \[proc\]\ allows\ packages\ to\ use\ sugar\ if\ they\ want\ without\ surprising\ packages\ that\ don't\ expect\ it.\ \ Packages\ that\ do\ want\ it\ can\ just\ do\ \[\[\[namespace\ import\]\ ::sugar::proc\]\]\ into\ their\ own\ private\ workspace.\n\n\[WHD\]:\ Since\ you\ have\ to\ override\ the\ standard\ control\ structures\ to\ make\ macros\ work,\ it\ seems\ to\ me\ that\ what\ you\ really\ need\ is\ a\ pair\ of\ commands:\n\n======\n\ sugar::configure\ -enabled\ 1\n\n\ #\ Macros\ expanded\ in\ body\n\ proc\ myproc\ \{a\ b\ c\}\ \{....\}\n\n\ #\ Macros\ expanded\ in\ expression\ and\ body\n\ while\ \{\$a\ >\ \$b\}\ \{....\}\n\n\ sugar::configure\ -enabled\ 0\n\n\ #\ Macros\ no\ long\ expanded.\n======\n\n\[SS\]:\ Actually\ \[Sugar\]\ does\ not\ override\ nothing!\ (so\ it\ will\ expand\ all\ at\ compile\ time,\ no\ run-time\ overhead).\nIt\ does\ expansion\ inside\ control\ structures\ just\ using\ macros\ for\ \[while\]\ and\ so\ on.\nIn\ this\ page\ this\ is\ better\ explained\ in\ the\ section:\ '''How\ macros\ knows\ what's\ a\ script?'''.\ So\ to\ override\ \[proc\],\ or\ to\ provide\ a\ private\nproc-like\ command\ is\ just\ a\ matter\ of\ design\ (or\ tastes),\ all\ will\ work\ in\ both\ the\ cases.\n\n<<categories>>\ Dev.\ Tools regexp2} CALL {my render {Sugar command macros} ***\ NOTE\ ***\n\ The\ API\ changed\ in\ two\ ways,\ you\ should\ be\ aware\ of\ this\ if\ you\ want\ to\ try\ with\ the\ current\ release\ of\n\ sugar\ what\ is\ shown\ in\ this\ tutorial.\n\n\ First\ Change\ -\ Now\ macros\ are\ expanded\ only\ inside\ procedures\ defined\ with\ ::sugar::proc\n\ Second\ Change\ -\ Now\ macros\ get\ a\ list\ of\ arguments\ like\ normal\ procedures,\ but\ the\ first\ argument\ is\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ the\ macro\ name\ itself.\ All\ the\ macros\ in\ this\ tutorial\ will\ work\ substituting\ the\ 'argv'\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ argument\ with\ 'args'.\n\n\ \ \ \ *\ Section\ 0\ -\ '''\[Sugar\]'''\n\ \ \ \ *\ Section\ 1\ -\ '''\[Sugar\ command\ macros\]'''\ (what\ you\ are\ reading)\n\ \ \ \ *\ Section\ 2\ -\ '''\[Sugar\ syntax\ macros\]'''\n\ \ \ \ *\ Section\ 3\ -\ '''\[Sugar\ transformers\]'''\n\n'''What\ is\ a\ Tcl\ macro'''.\n\nA\ macro\ is\ an\ operator\ implemented\ by\ transformation.\ Macros\ are\ procedures\nthat\ generate\ a\ Tcl\ program\ at\ compile\ time,\ and\ substitute\ it\ where\nthe\ programmer\ used\ thier\ name\ as\ command.\n\nIt's\ a\ simple\ concept\ if\ explained\ by\ examples.\n\nSuppose\ you\ want\ to\ write\ a\ \[\[clear\]\]\ command,\ that\ set\ the\ variable\nname\ passed\ as\ it's\ unique\ argument,\ to\ a\ null\ string.\ Implemented\nas\ a\ procedure\ using\ \[upvar\]\ it\ is\ like\ this:\n\n======\n\ proc\ clear\ varName\ \{\n\ \ \ \ \ upvar\ 1\ \$varName\ var\n\ \ \ \ \ set\ var\ \{\}\n\ \}\n======\n\nEvery\ time\ the\ user\ type\n\n======\n\ \ clear\ myvar\n======\n\nin\ a\ program,\ the\ \[\[clear\]\]\ procedure\ will\ be\ called,\ and\ the\ variable\nmyvar\ of\ the\ caller\ procedure\ will\ be\ set\ to\ a\ null\ string.\n\nAs\ an\ alternative\ to\ call\ a\ procedure\ that\ is\ able\ to\ alter\ the\ caller's\nexecution\ environment,\ we\ may\ want\ to\ automatically\ substitute\ every\ occurrence\nof\ the\ command\ \[\[clear\ <varname>\]\]\ with\ \[\[set\ <varname>\ \{\}\]\].\n\nSo\ basically\ we\ want\ that\ when\ we\ write\n\n======\n\ \ clearn\ myvar\n======\n\nin\ a\ program,\ it\ is\ substitute\ with\n\n======\n\ \ set\ myvar\ \{\}\n======\n\nas\ if\ the\ programmer\ had\ really\ typed\ \"set\ myvar\ \{\}\"\ instead\ of\ \"clear\ myvar\".\nThat's\ the\ goal\ of\ the\ simplest\ form\ of\ a\ \[Sugar\]'s\ macro.\n\nThe\ definition\ of\ a\ new\ macro\ is\ very\ similar\ to\ the\ creation\ of\ a\ procedure.\nThe\ following\ is\ the\ implementation\ of\ \[\[clear\]\]\ as\ a\ macro:\n\n======\n\ sugar::macro\ clear\ argv\ \{\n\ \ \ \ list\ set\ \[lindex\ \$argv\ 1\]\ \{\{\}\}\n\ \}\n======\n\nIt\ means:\ \"If\ you\ encounter\ a\ command\ called\ 'clear'\ inside\ the\ source\ code,\ncall\ the\ following\ procedure\ putting\ all\ the\ parts\ of\ which\ the\ncommand\ is\ composed\ in\ \$argv,\ and\ substitute\ the\ occurrence\ of\ the\ clear\ncommand\ and\ arguments,\ with\ what\ the\ procedure\ will\ return.\"\n\nAgain,\ with\ other\ words:\n\nSo,\ what\ happens\ is\ that\ when\ a\ procedure\ is\ compiled,\ for\ every\noccurrence\ of\ the\ \[\[clear\]\]\ command\ inside\ the\ procedure,\ the\ above\nprocedure\ is\ called,\ with\ \$argv\ set\ to\ a\ list\ that\ represents\ the\ arguments\nused\ to\ call\ the\ macro\ (including\ the\ macro\ name\ itself\ as\ first\ argument).\nThe\ result\ value\ of\ the\ function,\ that\ should\ be\ a\ list\ of\ the\ same\ form,\ is\nsubstituted\ in\ place\ of\ the\ original\ macro\ call.\n\nTo\ make\ the\ example\ more\ concrete,\ see\ the\ following\ code:\n\n======\n\ proc\ foobar\ \{\n\ \ \ \ \ set\ x\ 10\n\ \ \ \ \ clear\ x\n\ \}\n======\n\nBefore\ to\ compile\ the\ procedure,\ Tcl\ will\ call\ the\ macro\ we\ndefined\ with\ \$argv\ set\ to\ the\ two\ elements\ list\ \{clear\ x\}\ (verbatim).\nThat\ procedure\ returns\n\n======\n\ \ list\ set\ \[lindex\ \$argv\ 1\]\ \{\{\}\}\n======\n\nso\ for\ the\ argument\ \{clear\ x\}\ it\ will\ return\ the\ list\ \{set\ x\ \{\}\}.\nThis\ return\ value\ will\ be\ substituted\ in\ place\ of\ \"clear\ x\".\n\nActually,\ after\ the\ proc\ was\ defined,\ we\ can\ use\ \[\[info\ body\]\]\nto\ check\ what\ happened\ with\ the\ macro:\n\n======\n\ \ info\ body\ proc\n======\n\nwill\ output\n\n======\n\ \ \ \ \ set\ x\ 10\n\ \ \ \ \ set\ x\ \{\}\n======\n\nAt\ this\ point\ it's\ possible\ to\ use\ the\ \[\[clear\]\]\ macro\ as\ it\ was\ a\ Tcl\nprocedure.\n\nBut\ Tcl\ has\ \[\[uplevel\]\]\ and\ \[\[upvar\]\],\ so\ for\ what\ macros\nare\ useful?\ Fortuantely\ they\ allows\ for\ many\ interesting\ things\nnot\ possible\ at\ all\ otherwise,\ still,\ this\ example\nshows\ the\ first\ big\ advantage\ of\ macros:\n\n'''1)\ Macros\ makes\ Tcl\ faster,\ without\ to\ force\ the\ user\ to\ inline\ code\nby\ hand.'''\n\nThe\ \[\[clear\]\]\ command\ implemented\ as\ macro\ runs\ 3\ times\ faster\ in\ my\ Tcl\ 8.4.\n\nAlso,\ being\ \[\[upvar\]\]\ one\ of\ the\ biggest\ obstacles\ in\ the\ ability\nof\ the\ Tcl\ compiler\ to\ optimize\ Tcl\ bytecode,\ it's\ not\ impossible\nthat\ at\ some\ point\ Tcl\ will\ be\ able\ to\ run\ much\ faster\nif\ the\ user\ will\ ensure\ a\ given\ procedure\ is\ never\ target\ of\ \[\[upvar\]\].\n\nSimple\ commands\ that\ involve\ the\ use\ of\ upvar\ can\ be\ even\nmore\ simple\ to\ write\ implemented\ as\ macros.\ The\ following\ are\ four\ examples:\n\n======\n\ #\ \[first\ \$list\]\ -\ expands\ to\ \[lindex\ \$list\ 0\]\n\ sugar::macro\ first\ argv\ \{\n\ \ \ \ list\ lindex\ \[lindex\ \$argv\ 1\]\ 0\n\ \}\n\n\ #\ \[rest\ \$list\]\ -\ expands\ to\ \[lrange\ \$list\ 1\ end\]\n\ sugar::macro\ rest\ argv\ \{\n\ \ \ \ list\ lrange\ \[lindex\ \$argv\ 1\]\ 1\ end\n\ \}\n\n\ #\ \[last\ \$list\]\ -\ expands\ to\ \[lindex\ \$list\ end\]\n\ sugar::macro\ last\ argv\ \{\n\ \ \ \ list\ lindex\ \[lindex\ \$argv\ 1\]\ end\n\ \}\n\n\ #\ \[drop\ \$list\]\ -\ expands\ to\ \[lrange\ \$list\ 0\ end-1\]\n\ sugar::macro\ drop\ argv\ \{\n\ \ \ \ list\ lrange\ \[lindex\ \$argv\ 1\]\ 0\ end-1\n\ \}\n======\n\n\[Sugar\]\ supports\ three\ types\ of\ macros.\ We\ are\ dealing\ with\nthe\ simplest\ and\ more\ common\ macros:\ command\ macros.\n\nThe\ other\ two\ types,\ syntax\ macros,\ and\ transformers,\nwill\ be\ covered\ later.\ For\ now\ let's\ go\ to\ create\na\ more\ complex\ macro.\n\n'''A\ more\ complex\ example'''\n\nGood\ macros\ do\ source\ code\ transformation\ in\ a\ smart\ way,\nthey\ turn\ a\ form\ that\ is\ undestood\ by\ the\nprogrammer,\ to\ one\ that\ is\ also\ understood\ by\ the\ compiler,\nthat's\ hard\ to\ type\ and\ use\ in\ raw\ form\ without\ the\nmacro\ support,\ but\ optimal\ otherwise.\n\nIdeally\ a\ macro\ should\ expand\ to\ a\ single\ command\ call\n(possibly\ including\ many\ other\ nested),\ and\ should\ not\nexpand\ to\ code\ that\ magically\ creates\ variables\ at\nruntime\ to\ store\ intermediate\ results\ all\ the\ times\ it\ncan\ be\ avoided\ (because\ there\ may\ be\ collisions\ with\nvariables\ in\ the\ function,\ or\ created\ by\ other\ bad\ macros.\nBtw,\ in\ the\ TODO\ list\ of\ sugar\ there\ is\ a\ way\ to\ generate\nunique\ local\ variable\ names).\n\nIf\ the\ macro\ is\ well\ written,\ then\ the\ programmer\ can\ use\ it\ like\nany\ other\ command\ without\ to\ care\ much.\n\nWe\ will\ see\ a\ more\ real\ example\ of\ macro\ that\ implements\nan\ very\ efficient\ \[\[lpop\]\]\ operator.\ It\ accepts\ only\ one\nargument,\ the\ name\ of\ a\ variable,\ and\ returns\ the\ last\nelement\ of\ the\ list\ stored\ inside\ the\ given\ variable.\nAs\ side\ effect,\ \[\[lpop\]\]\ removes\ the\ last\ element\ from\ the\ list.\n(it's\ something\ like\ the\ complementar\ of\ \[lappend\]).\n\nA\ pure-Tcl\ implementation\ is\ the\ following:\n\n======\n\ proc\ lpop\ listVar\ \{\n\ \ \ \ upvar\ 1\ \$listVar\ list\n\ \ \ \ set\ res\ \[lindex\ \$list\ end\]\n\ \ \ \ set\ list\ \[lrange\ \$list\ 1\ end\]\n\ \ \ \ return\ \$res\n\ \}\n======\n\nThis\ version\ of\ lpop\ is\ really\ too\ slow,\ in\ fact\ when\n\[\[lrange\]\]\ is\ called,\ it\ creates\ a\ new\ list\ object\ even\nif\ the\ original\ one,\ stored\ in\ the\ \$list\ variable,\ is\ going\nto\ be\ freed\ and\ replaced\ by\ the\ copy.\ To\ modify\ the\ list\non-place\ is\ far\ better.\n\nThe\ \[\[lrange\]\]\ implementation\ is\ able\ to\ perform\ this\noptimization\ if\ the\ object\ in\ \"not\ shared\"\ (if\ you\ don't\nknow\ about\ this\ stuff\ try\ to\ read\ the\ Wiki\ page\ about\ the\n\[K\]\ operator\ before\ to\ continue)\n\nSo\ it's\ better\ to\ write\ the\ proc\ using\ the\ \[K\]\ operator.\nThe\ lrange\ line\ should\ be\ changed\ to\ this:\n\n======\n\ \ \ \ set\ list\ \[lrange\ \[K\ \$list\ \[set\ list\ \"\"\]\]\ 1\ end\]\n======\n\nWith\ K\ being:\n\n======\n\ proc\ K\ \{x\ y\}\ \{\n\ \ \ \ \ return\ \$x\n\ \}\n======\n\nBut\ even\ to\ call\ \[K\]\ is\ costly\ in\ terms\ of\ performace,\ so\nwhy\ don't\ inline\ it\ also?\ To\ do\ it\ requires\ to\ change\nthe\ previous\ lrange\ line\ to\ this:\n\n======\n\ \ \ \ set\ list\ \[lrange\ \[lindex\ \[list\ \$list\ \[set\ list\ \"\"\]\]\ 0\]\ 1\ end\]\n======\n\nThat's\ really\ a\ mess\ to\ read,\ but\ works\ at\ different\ speed,\ and\neven\ more\ important,\ at\ different\ time\ complexity!.\n\nWriting\ a\ macro\ for\ \[\[lpop\]\]\ we\ can\ go\ even\ faster,\ and,\ at\ the\nsame\ time,\ we\ can\ have\ this\ code\ more\ easy\ to\ maintain\ and\nread.\ Actually\ macros\ are\ allowed\ to\ expand\ to\ commands\ containing\nother\ macros,\ and\ so\ on.\ This\ means\ that\ we\ can\ write\ a\ macro\nfor\ every\ single\ step\ of\ \[\[lpop\]\].\ We\ need\ the\ \[\[first\]\]\n\[\[last\]\]\ and\ \[\[drop\]\]\ macros\ already\ developed,\ and\ a\ macro\ for\ \[K\]:\n\n======\n\ sugar::macro\ K\ argv\ \{\n\ \ \ \ foreach\ \{x\ y\}\ \$argv\ break\n\ \ \ \ list\ first\ \"\[list\ \$x\ \$y\]\"\n\ \}\n======\n\nNote\ that\ we\ used\ \[foreach\]\ instead\ of\ two\ calls\ to\ \[lindex\]\nthat's\ probably\ faster.\ But\ remember\ that\ macros\ don't\ have\nto\ be\ fast\ in\ the\ *generation*\ of\ the\ expanded\ code.\n\nThis\ will\ expand\ \[\[K\ \$x\ \$y\]\]\ into\ \[\[first\ \[\[list\ \$x\ \$y\]\]\]\],\ that\ will\nbe\ expanded\ in\ \[\[lindex\ \[\[list\ \$x\ \$y\]\]\ 0\]\].\n\nWe\ have\ one\ last\ problem.\ Even\ after\ the\ optimization\ and\ the\nuse\ of\ \[\[K\]\]\ inline,\ the\ procedure\ above\ required\ a\ local\nvariable\ 'res'\ to\ save\ the\ last\ argument\ of\ the\ list\ before\nto\ modify\ it,\ and\ use\ \$res\ later\ as\ return\ value\ for\ the\ procedure.\nWe\ don't\ want\ to\ create\ local\ vars\ into\ the\ code\ that\ calls\nthe\ \[\[lpop\]\]\ macro,\ nor\ we\ want\ to\ expand\ to\ more\ than\ a\ single\ncommand.\ The\ \[K\]\ operator\ can\ help\ us\ to\ do\ so.\ Instead\ to\nuse:\n\n======\n\ \ \ \ set\ res\ \[lindex\ \$list\ end\]\n\ \ \ \ set\ list\ \[lrange\ \[lindex\ \[list\ \$list\ \[set\ list\ \"\"\]\]\ 0\]\ 1\ end\]\n\ \ \ \ return\ \$res\n======\n\nwhy\ don't\ just\ write:\n\n======\n\ \ \ \ K\ \[lindex\ \$list\ end\]\ \[set\ list\ \[lrange\ \[lindex\ \[list\ \$list\ \[set\ list\ \"\"\]\]\ 0\]\ 1\ end\]\]\n======\n\nThat's\ ok,\ but\ what\ an\ unreadable\ code!\ Thanks\ to\ amcros\nwe\ can\ abstract\ from\ the\ fact\ that\ to\ call\ procedures\ is\nslow,\ so\ we\ just\ write:\n\n======\n\ \ \ \ \[K\ \[last\ \$list\]\ \[set\ list\ \[rest\ \[K\ \$list\ \[set\ list\ \"\"\]\]\]\]\]\n======\n\nWill\ not\ win\ the\ clean-code\ context\ this\ year,\ but\ it's\ much\nbetter\ than\ the\ previous.\ Ok...\ now\ we\ want\ a\ macro\ that,\ every\ time\ we\ntype\ \"lpop\ \$list\",\ will\ expand\ in\ the\ above\ line:\n\n======\n\ sugar::macro\ lpop\ argv\ \{\n\ \ \ \ set\ varname\ \[lindex\ \$argv\ 1\]\n\ \ \ \ set\ argv\ \[list\ \\\n\ \ \ \ \ \ \ \ K\ \\\n\ \ \ \ \ \ \ \ \{\[last\ \$%varname%\]\}\ \\\n\ \ \ \ \ \ \ \ \{\[set\ list\ \[drop\ \[K\ \$%varname%\ \[set\ %varname%\ \"\"\]\]\]\]\}\ \\\n\ \ \ \ \]\n\ \ \ \ foreach\ i\ \{1\ 2\}\ \{\n\ \ \ \ \ \ \ \ lset\ argv\ \$i\ \[string\ map\ \[list\ %varname%\ \$varname\]\ \[lindex\ \$argv\ \$i\]\]\n\ \ \ \ \}\n\ \ \ \ return\ \$argv\n\ \}\n======\n\[MDR\]:\ Why\ not\ just:\n\ sugar::macro\ lpop\ argv\ \{\n\ \ \ \ string\ map\ \[list\ %varname%\ \[lindex\ \$argv\ 1\]\]\ \{K\ \{\[last\ \$%varname%\]\}\ \{\[set\ list\ \[drop\ \[K\ \$%varname%\ \[set\ %varname%\ \"\"\]\]\]\]\}\}\n\ \}\n======\n\nThere\ are\ few\ things\ to\ note\ about\ this\ code.\ The\ macro\ returns\na\ list,\ where\ every\ element\ is\ a\ token\ of\ a\ Tcl\ command\nin\ the\ source\ code.\ This\ does\ not\ mean\ we\ have\ to\ transform\ in\nlists\ even\ arguments\ that\ happens\ to\ represent\ a\ script.\ Also\nnote\ that\ the\ input\ list\ of\ the\ macro\ is\ just\ a\ list\ of\ tokens\nthat\ are\ *exactly*\ what\ the\ user\ typed\ they\ in\ the\ source\ code,\ verbatim.\nWhat\ follows\ is\ that\ the\ tokens\ are\ already\ quoted\ and\ valid\nrepresentations\ of\ a\ procedure\ argument.\nWe\ don't\ need\ to\ care\ about\ the\ fact\ that\ they\ must\ be\ interpreted\nas\ a\ single\ argument\ like\ if\ we\ were\ generating\ code\ to\ pass\ to\ \[eval\].\n\nThis\ allows\ the\ macro\ developer\ to\ use\ templates\ for\ macros,\ in\nfact\ the\ \[\[lpop\]\]\ macro\ is\ just\ using\ a\ three\ argument\ template,\nand\ the\ final\ foreach\ will\ substitute\ the\ arguments\ that\ needs\nto\ refer\ to\ the\ variable\ name,\ with\ that\ name.\ You\ don't\ have\nto\ care\ what\ that\ variable\ name\ is.\ It\ can\ be\ a\ complex\ string\nformed\ by\ more\ commands,\ vars,\ and\ so\ on\ \[\[like\]\]\[\[this\]\]\$and-this.\nIf\ it\ was\ a\ single\ argument\ in\ the\ source\ code,\ it\ will\ be\ in\ the\ macro\nafter\ the\ expansion.\n\nAnother\ interesting\ thing\ to\ note\ is\ that\ we\ don't\ really\ have\nto\ return\ every\ token\ as\ a\ different\ element\ of\ the\ list.\ In\ pratice\nwe\ can\ return\ it\ even\ as\ a\ single-element\ list.\nThe\ rule\ is\ that\ the\ macro\ expander\ will\ care\ to\ put\ an\ argument\nseparator\ like\ a\ tab,\ or\ a\ space,\ for\ every\ element\ of\ the\nlist,\ and\ put\ a\ command\ separator\ like\ newline\ or\ \"\;\"\ at\ the\ end.\nIf\ we\ put\ spaces\ ourself,\ we\ can\ just\ return\ a\ single\ element\ list.\n\nSo,\ the\ lpop\ macro\ can\ also\ by\ written\ in\ this\ way:\n\n======\n\ sugar::macro\ lpop\ argv\ \{\n\ \ \ \ set\ varname\ \[lindex\ \$argv\ 1\]\n\ \ \ \ set\ cmd\ \[format\ \{\n\ \ \ \ \ \ \ \ K\ \[last\ \$%varname%\]\ \[set\ list\ \[drop\ \[K\ \$%varname%\ \[set\ %varname%\ \"\"\]\]\]\]\n\ \ \ \ \}\ \$varname\ \$varname\ \$varname\]\n\ \ \ \ return\ \[list\ \$cmd\]\n\ \}\n======\n\nThis\ is\ much\ more\ simple\ and\ clean,\ and\ actually\ it's\ possible\ to\nuse\ this\ style.\ The\ difference\ is\ that\ returning\ every\ token\ as\na\ different\ element\ of\ a\ list\ makes\ \[Sugar\]\ macros\ able\ to\nleft\ the\ indentation\ of\ the\ original\ code\ unaltered.\ This\ is\ helpful\nboth\ to\ take\ procedure\ error's\ line\ numbers\ correct,\ and\ to\nsee\ a\ good\ locking\ output\ of\ \[info\ body\].\ But\ as\ long\ as\nmost\ macros\ are\ about\ commands\ that\ are\ just\ typed\ in\ the\ same\ line\ntogether\ with\ all\ the\ arguments,\ for\ many\ macros\ is\ just\ a\ matter\ of\ tastes.\n\nIf\ you\ are\ implementing\ control\ structures\ that\ are:\n\n======\n\ indented\ \{in\}\ \{\n\ \ \ \ this\ way\n\ \}\n======\n\nIt's\ another\ question,\ and\ it's\ better\ to\ return\ every\ token\ as\ a\nlist\ element.\n\n'''Number\ of\ argument\ and\ other\ static\ checks\ in\ macros'''\n\nMacros\ expand\ to\ code\ that\ will\ raise\ an\ error\ if\ the\ number\ of\narguents\ is\ wrong\ in\ most\ cases,\ but\ it's\ possible\ to\ add\ this\ncontrol\ inside\ the\ macro.\ Actually\ it's\ a\ big\ advantage\ of\ macros\nbecause\ they\ are\ able\ to\ signal\ a\ bad\ number\ of\ arguments\ at\nrun\ time:\ this\ can\ help\ to\ write\ applications\ that\ are\ more\ reliable.\nIt's\ even\ possible\ to\ write\ a\ macro\ that\ expands\ to\ exactly\ what\nthe\ user\ typed\ in,\ but\ as\ side\ effect\ does\ a\ static\ check\ for\nbad\ number\ (or\ format)\ of\ arguments:\n\n======\n\ sugar::macro\ set\ argv\ \{\n\ \ \ \ if\ \{\[llength\ \$argv\]\ !=\ 3\ ||\ \[llength\ \$argv\]\ !=\ 2\}\ \{\n\ \ \ \ \ \ \ \ error\ \"Bad\ number\ of\ arguments\ for\ set\"\n\ \ \ \ \}\n\ \ \ \ return\ \$argv\n\ \}\n======\n\nThis\ macro\ returns\ \$argv\ itself,\ so\ it's\ an\ identity\ transformation,\nbut\ will\ raise\ errors\ for\ \[\[set\]\]\ with\ a\ bad\ number\ of\narguments\ even\ for\ code\ that\ will\ never\ be\ reached\nin\ the\ application.\ Note\ that\ the\ previous\ macro\ for\ set\ is\ a\ bit\nincomplete:\ to\ get\ it\ right\ we\ should\ add\ checks\ for\ arguments\nthat\ starts\ with\ \[\{*\}\],\ for\ this\ reason\ \[Sugar\]\ will\ provide\ a\ function\nto\ automatically\ search\ for\ a\ bad\ number\ of\ arguments\ in\ some\nnext\ version.\n\nNote\ that\ \{*\}\ introduces\ for\ the\ first\ time\ the\ possibility\ for\na\ command\ to\ get\ a\ number\ of\ arguments\ that\ is\ non\ evident\ reading\nthe\ source\ code\ but\ computed\ at\ runtime.\ Actually\ \{*\}\ is\ an\nadvantage\ for\ static\ checks\ because\ before\ of\ it\ the\ way\ to\ngo\ was\ \[\[eval\]\],\ that\ does\ totally\ \"hide\"\ the\ called\ command\ postponing\nall\ the\ work\ at\ run-time.\ With\ \{*\}\ it's\ always\ possible\nto\ say\ from\ the\ source\ code\ that\ a\ command\ is\ called\ with\ *at\ least*\ N\narguments.\ Still,\ to\ add\ new\ syntax\ to\ Tcl\ will\ probably\ not\ play\nwell\ with\ macros\ and\ other\ form\ of\ source\ code\ processing.\n\nIdentity\ macros\ are\ very\ powerful\ to\ perform\ static\ syntax\ checks,\nthey\ can\ not\ only\ warn\ on\ bad\ number\ of\ arguments,\ but\ with\ the\ntype\ of\ this\ arguments.\ See\ for\ example\ the\ following\ identity\nmacro\ for\ \"string\ is\":\n\n======\n\ proc\ valid_string_class\ class\ \{\n\ \ \ \ set\ classes\ \{alnum\ alpha\ ascii\ control\ boolean\ digit\ double\ false\ graph\ integer\ lower\ print\ punct\ space\ true\ upper\ wordchar\ xdigit\}\n\ \ \ \ set\ first\ \[string\ index\ \$class\ 0\]\n\ \ \ \ if\ \{\$first\ eq\ \{\$\}\}\ \{return\ 1\}\n\ \ \ \ if\ \{\$first\ eq\ \{\[\}\}\ \{return\ 1\}\n\ \ \ \ if\ \{\[lsearch\ \$classes\ \$class\]\ !=\ -1\}\ \{return\ 1\}\n\ \ \ \ return\ 0\n\ \}\n\n\ sugar::macro\ string\ argv\ \{\n\ \ \ \ if\ \{\[lindex\ \$argv\ 1\]\ eq\ \{is\}\ &&\ \[llength\ \$argv\]\ >\ 2\}\ \{\n\ \ \ \ \ \ \ \ if\ \{!\[valid_string_class\ \[lindex\ \$argv\ 2\]\]\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ puts\ stderr\ \"Warning:\ invalid\ string\ class\ in\ procedure\ \[sugar::currentProcName\]\"\n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \}\n\ \ \ \ return\ \$argv\n\ \}\n======\n\nThanks\ to\ this\ macro\ it's\ possible\ to\ ensure\ that\ errors\ like\ to\ write\n\[\[string\ is\ number\]\]\ instead\ \[\[string\ is\ integer\]\]\ are\ discovered\ at\ncompile-time.\ In\ this\ respect\ macros\ can\ be\ seen\ as\ a\ programmable\nstatic\ syntax\ checker\ for\ Tcl.\ We\ will\ see\ how\ \"syntax\ macros\"\ are\neven\ more\ useful\ in\ this\ respect.\ This\ is\ the\ second\ feature\ that\nmacros\ add\ to\ Tcl:\n\n'''2)\ Macros\ are\ a\ powerful\ programmable\ static\ checker\ for\ Tcl\ scripts.'''\n\nActually\ I\ think\ it's\ worth\ to\ use\ macros\ even\ only\ for\ this\ during\nthe\ development\ process,\ and\ than\ flush\ they\ away.\n\n'''Conditional\ compilation'''\n\nThat's\ small\ and\ neat:\ we\ can\ write\ a\ simple\ macro\ that\ expands\ to\nsome\ code\ only\ if\ a\ global\ variable\ is\ set\ to\ non-zero.\ Let's\nwrite\ this\ macro\ that\ we\ call\ \[\[debug\]\].\n\n======\n\ sugar::macro\ debug\ argv\ \{\n\ \ \ \ if\ \{\$::debug_mode\}\ \{\n\ \ \ \ \ \ \ \ list\ if\ 1\ \[lindex\ \$argv\ 1\]\n\ \ \ \ \}\ else\ \{\n\ \ \ \ \ \ \ \ list\n\ \ \ \ \}\n\ \}\n======\n\nThan\ you\ can\ use\ it\ in\ your\ application\ like\ if\ it\ was\ a\ conditional:\n\n======\n\ #\ Your\ application\ ...\n\ debug\ \{\n\ \ \ \ set\ c\ 0\n\ \}\n\ while\ 1\ \{\n\ \ \ \ debug\ \{\n\ \ \ \ \ \ \ \ incr\ c\n\ \ \ \ \ \ \ \ if\ \{\$c\ >\ 100\}\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ error\ \"Too\ many\ iteractions...\"\n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \}\n\ \ \ \ ....\ do\ something\ ....\n\ \}\n======\n\nAll\ the\ \[\[debug\ \{someting\}\]\]\ commands\ are\ compiled\ as\ \[\[if\ 1\ \{something\}\]\]\nif\ the\ ::debug_mode\ variable\ is\ true.\ Instead\ if\ this\ var\ is\ false,\nthey\ will\ not\ be\ compiled\ at\ all.\n\nThat's\ the\ simplest\ example,\ you\ can\ write\ similar\ macros\ like\ \[\[ifunix\]\],\n\[\[ifwindows\]\],\ \[\[ifmac\]\],\ or\ even\ to\ expand\ to\ different\ procedures\ call\nif\ a\ given\ command\ is\ called\ with\ 2,\ 3\ or\ 4\ arguments.\ The\ limit\ is\nthe\ immagination.\n\n'''New\ control\ stuctures'''\n\nNot\ all\ the\ programming\ languages\ allow\ to\ write\ new\ control\ structures.\nTcl\ is\ one\ of\ this\ better\ languages\ that\ don't\ put\ the\ programmer\ninside\ a\ jail,\ but,\ not\ all\ the\ programming\ languages\ that\ allows\nto\ write\ new\ control\ structures,\ are\ able\ to\ make\ they\ efficient.\n\nTcl\ macros\ can\ make\ new\ control\ sturctures\ as\ fast\ as\ byte\ compiled\ncontrol\ structures,\ because\ user\ defined\ ones\ are\ usually\ syntax\ glue\nfor\ code\ transformations.\ Being\ macro\ transformers\nthat\ translates\ a\ from\ to\ another,\ that's\ a\ good\ fit\ for\ macros.\n\nThat's\ a\ macro\ for\ the\ ?:\ operator.\n\n======\n\ #\ ?:\ expands\n\ #\ \ \ ?:\ cond\ val1\ val2\n\ #\ to\n\ #\ \ \ if\ \$cond\ \{format\ val1\}\ \{format\ val2\}\n\ sugar::macro\ ?:\ argv\ \{\n\ \ \ \ if\ \{\[llength\ \$argv\]\ !=\ 4\}\ \{\n\ \ \ \ \ \ \ \ error\ \"Wrong\ number\ of\ arguments\"\n\ \ \ \ \}\n\ \ \ \ foreach\ \{_\ cond\ val1\ val2\}\ \$argv\ break\n\ \ \ \ list\ if\ \$cond\ \[list\ \[list\ format\ \$val1\]\]\ \[list\ \[list\ format\ \$val2\]\]\n\ \}\n======\n\nThe\ macro's\ comment\ shows\ the\ expansion\ performed.\nBeing\ it\ translated\ to\ an\ \[if\]\ command,\ it's\ as\ fast\ as\ a\nTcl\ builtin.\n\n'''How\ macros\ knows\ what's\ a\ script?'''\n\nI\ Tcl\ there\ are\ no\ types,\ nor\ special\ syntaxes\ for\ what\ is\ code\nand\ what\ is\ just\ a\ string,\ so\ you\ may\ wonder\ why\ macros\ are\nnot\ expanded\ in\ the\ following\ code:\n\n======\n\ puts\ \{\n\ \ \ \ set\ foo\ \{1\ 2\ 3\}\;\ \[first\ \$foo\]\n\ \}\n======\n\nBut\ they\ are\ expanded\ in\ this:\n\n======\n\ while\ 1\ \{\n\ \ \ \ set\ foo\ \{1\ 2\ 3\}\;\ \[first\ \$foo\]\n\ \}\n======\n\nI\ guess\ this\ is\ one\ of\ the\ main\ problems\ developers\ face\ designing\ a\nmacro\ system\ for\ Tcl,\ and\ even\ one\ of\ the\ better\ helpers\ of\ the\ idea\nthat\ a\ good\ macro\ system\ for\ Tcl\ is\ impossible\ because\ you\ can't\nsay\ what\ is\ code\ and\ what\ isn't.\n\n\[Sugar\]\ was\ designed\ to\ address\ this\ problem\ in\ the\ simplest\ possible\nof\ the\ ways:\ because\ it\ can't\ say\ if\ an\ argument\ is\ a\ script\ or\ not,\nmacro\ expansion\ is\ not\ performed\ in\ arguments,\ so\ in\ theory\ \[Sugar\]\nwill\ not\ expand\ the\ code\ that's\ argument\ to\ \[puts\],\ nor\ \[while\].\n\nBut\ of\ course,\ in\ the\ real\ world\ for\ a\ macro\ system\ to\ be\ usable,\nmacros\ should\ be\ expanded\ inside\ the\ \[while\],\ and\ not\ expanded\ in\ \[puts\],\nso\ the\ idea\ is\ that\ for\ commands\ you\ know\ the\ argument\ is\ a\ script,\nyou\ write\ a\ macro\ that\ returns\ the\ same\ command\ but\ with\nscript\ arguments\ macro-expanded.\ It\ is\ very\ simple\ and\ in\ pratice\nthis\ works\ well.\ For\ example\ that's\ the\ macro\ for\ \[while\]:\n\n======\n\ sugar::macro\ while\ argv\ \{\n\ \ \ \ lset\ argv\ 1\ \[sugar::expandExprToken\ \[lindex\ \$argv\ 1\]\]\n\ \ \ \ lset\ argv\ 2\ \[sugar::expandScriptToken\ \[lindex\ \$argv\ 2\]\]\n\ \}\n======\n\nThat's\ the\ macro\ for\ \[if\]:\n\n======\n\ sugar::macro\ if\ argv\ \{\n\ \ \ \ lappend\ newargv\ \[lindex\ \$argv\ 0\]\n\ \ \ \ lappend\ newargv\ \[sugar::expandExprToken\ \[lindex\ \$argv\ 1\]\]\n\ \ \ \ set\ argv\ \[lrange\ \$argv\ 2\ end\]\n\ \ \ \ foreach\ a\ \$argv\ \{\n\ \ \ \ \ \ \ \ switch\ --\ \$a\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ else\ -\ elseif\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ lappend\ newargv\ \$a\n\ \ \ \ \ \ \ \ \ \ \ \ \}\ \n\ \ \ \ \ \ \ \ \ \ \ \ default\ \{\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ lappend\ newargv\ \[sugar::expandScriptToken\ \$a\]\n\ \ \ \ \ \ \ \ \ \ \ \ \}\n\ \ \ \ \ \ \ \ \}\n\ \ \ \ \}\n\ \ \ \ return\ \$newargv\n\ \}\n======\n\nAs\ you\ can\ see\ \[Sugar\]\ exports\ an\ API\ to\ perform\ expansion\ in\nTcl\ scripts\ and\ Expr\ expressions.\ There\ are\ similar\ macros\ for\n\[switch\],\ \[for\],\ and\ so\ on.\ If\ you\ write\ a\ new\ conditional\ or\nloop\ command\ with\ macros,\ you\ don't\ need\ it\ at\ all\ because\nthe\ macro\ will\ translate\ to\ code\ that\ contains\ some\ form\ of\na\ well\ known\ built-in\ conditional\ or\ loop\ command,\ and\ we\ already\nhave\ macros\ for\ this\ (remember\ that\ macros\ can\ return\ code\ with\nmacros).\n\nIf\ you\ write\ any\ other\ command\ that\ accept\ as\ arguments\ a\ Tcl\ script\nor\ expr\ expression,\ just\ write\ a\ little\ macro\ for\ it\ to\ do\nmacro\ expansion.\ This\ has\ a\ nice\ side\ effect:\n\n======\n\ proc\ nomacro\ script\ \{\n\ \ \ \ uplevel\ 1\ \$script\n\ \}\n======\n\nDon't\ write\ a\ macro\ for\ nomacro,\ and\ you\ have\ a\ ready-to-use\ncommand\ that\ works\ as\ a\ barrier\ for\ macro\ expansion.\n\nContinue\ with\ section\ 2\ -\ '''\[Sugar\ syntax\ macros\]'''\n\n----\n\n\[WHD\]:\ This\ is\ very\ cool,\ but\ I\ have\ to\ ask--why\ not\ allow\ macros\ to\nhave\ a\ standard\ Tcl\ argument\ list?\ \ That\ is,\ \n\n======\n\ sugar::macro\ mymacro\ \{args\}\ \{...\}\n======\n\nGives\ the\ behavior\ you\ describe\ here,\ while\n\n======\n\ sugar::macro\ mymacro\ \{a\ b\ c\}\ \{...\}\n======\n\nexplicitly\ creates\ a\ macro\ that\ takes\ three\ arguments\ and\ will\ngenerate\ a\ standard\ error\ message\ if\ you\ supply\ some\ other\ number?\n\n----\n\n\[SS\]:\ This\ can\ be\ a\ good\ idea,\ being\ always\ possible\ to\nuse\ 'args'\ as\ only\ argument\ to\ have\ the\ current\ behaviour.\ I\ used\na\ single\ list\ as\ input\ mainly\ because\ the\ same\ macro\ can\ have\ more\nthen\ a\ name,\ and\ in\ order\ to\ have\ the\ same\ interface\ for\ both\ncommand\ macros\ and\ syntax\ macros.\ For\ example:\n\n======\n\ sugar::macro\ \{*\ +\ -\ /\}\ argv\ \{\n\ \ \ \ list\ expr\ \[list\ \[join\ \[lrange\ \$argv\ 1\ end\]\ \"\ \[lindex\ \$argv\ 0\]\ \"\]\]\n\ \}\n======\n\nWill\ handle\ *\ +\ -\ /\ with\ the\ same\ code.\ Macros\ with\ more\ than\ a\ name\nmay\ in\ extreme\ cases\ even\ give\ different\ meanings\ for\ arguments\ in\nthe\ same\ position.\ Btw\ there\ is\ 'args'\ for\ this\ case.\ So\ I\ can\ change\nthe\ API\ to\ something\ like\ this:\n\n======\n\ \ sugar::macro\ \{name\ arg1\ arg2\ ...\}\ \{...\}\n======\n\nThat's\ like\ a\ Tcl\ proc,\ but\ with\ the\ name\ that\ was\ used\ to\ call\ the\ macro\nas\ the\ first\ argument.\ For\ syntax\ macros\ this\ format\ actually\ may\ not\ have\ a\nlot\ of\ sense,\ but\ there\ is\ still\ 'args'.\ I'll\ include\ this\ chage\ in\ the\nnext\ version\ if\ I'll\ not\ receive\ feedbacks\ against\ it.\ Thanks\ for\ the\nfeedback\ WHD.\n\n\[WHD\]:\ I\ think\ that\ on\ the\ whole\ I\ prefer\ the\ previous\ syntax\ for\ command\nmacros\;\ the\ macro\ can\ always\ have\ an\ implicit\ argument\ that\ is\ the\nmacro\ name.\ \ For\ example,\n\n======\n\ #\ Identity\ macro\n\ sugar::macro\ \{+\ -\ *\ /\}\ \{args\}\ \{\ return\ \"\$macroname\ \$args\"\ \}\n======\n\n\[JMN\]:\ I'd\ just\ like\ to\ add\ my\ vote\ for\ removing\ the\ macroname\ as\ first\ argument\ syntax.\nFrom\ my\ hacking\ about,\ it\ seems\ easy\ to\ make\ it\ implicitly\ available\ more\ or\ less\ as\ WHD\ suggests.\n(I\ don't\ *think*\ I\ broke\ anything..\ )\n----\n\n\[SS\]:\ For\ a\ different\ question\ about\ the\ sugar\ API,\ I\ wonder\ if\ Tclers\ninterested\ in\ this\ macro\ system\ feel\ better\ the\ current\ redefinition\nof\ \[proc\],\ or\ if\ it's\ better\ to\ provide\ a\ sugar::proc\ procedure\ that's\nexactly\ like\ \[proc\]\ but\ with\ macro\ expansion.\n\nIf\ the\ API\ will\ remain\ the\ current\ with\ \[proc\]\ redefined,\ I'll\ add\nin\ the\ wrapper\ an\ option\ -nomacro\ that\ will\ just\ call\ the\ original\ proc.\nPlease\ add\ your\ name\ with\ optional\ motivation\ below.\n\nYes,\ I\ think\ it's\ better\ to\ wrapper\ the\ real\ \[proc\]:\n\ \ \ \ *\ Put\ your\ name\ here\ if\ you\ are\ for\ this\ solution.\n\nNo,\ I\ want\ macro\ expansion\ only\ using\ sugar::proc:\n\ \ \ \ *\ \[SS\]\ (avoid\ to\ waste\ CPU\ time\ for\ procs\ that\ don't\ use\ macros,\ this\ can\ be\ a\ big\ difference\ if\ you\ \[package\ require\]\ sugar\ before\ Tk\ or\ other\ big\ packages)\n\ \ \ *\ \[DKF\]:\ Avoiding\ overriding\ the\ real\ \[proc\]\ allows\ packages\ to\ use\ sugar\ if\ they\ want\ without\ surprising\ packages\ that\ don't\ expect\ it.\ \ Packages\ that\ do\ want\ it\ can\ just\ do\ \[\[\[namespace\ import\]\ ::sugar::proc\]\]\ into\ their\ own\ private\ workspace.\n\n\[WHD\]:\ Since\ you\ have\ to\ override\ the\ standard\ control\ structures\ to\ make\ macros\ work,\ it\ seems\ to\ me\ that\ what\ you\ really\ need\ is\ a\ pair\ of\ commands:\n\n======\n\ sugar::configure\ -enabled\ 1\n\n\ #\ Macros\ expanded\ in\ body\n\ proc\ myproc\ \{a\ b\ c\}\ \{....\}\n\n\ #\ Macros\ expanded\ in\ expression\ and\ body\n\ while\ \{\$a\ >\ \$b\}\ \{....\}\n\n\ sugar::configure\ -enabled\ 0\n\n\ #\ Macros\ no\ long\ expanded.\n======\n\n\[SS\]:\ Actually\ \[Sugar\]\ does\ not\ override\ nothing!\ (so\ it\ will\ expand\ all\ at\ compile\ time,\ no\ run-time\ overhead).\nIt\ does\ expansion\ inside\ control\ structures\ just\ using\ macros\ for\ \[while\]\ and\ so\ on.\nIn\ this\ page\ this\ is\ better\ explained\ in\ the\ section:\ '''How\ macros\ knows\ what's\ a\ script?'''.\ So\ to\ override\ \[proc\],\ or\ to\ provide\ a\ private\nproc-like\ command\ is\ just\ a\ matter\ of\ design\ (or\ tastes),\ all\ will\ work\ in\ both\ the\ cases.\n\n<<categories>>\ Dev.\ Tools} CALL {my revision {Sugar command macros}} CALL {::oo::Obj4183552 process revision/Sugar+command+macros} CALL {::oo::Obj4183550 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