Макросы используются не так часто, поскольку uplevel
, upvar
и tailcall
предоставляют набор возможностей, которые работают довольно хорошо.В частности, использование uplevel
не препятствует компиляции байт-кода (с учетом нескольких технических ограничений и при условии, что вы используете 8.6 или более позднюю версию), поэтому вы можете продолжать и использовать это, не беспокоясь слишком сильно.Это не означает, что макросы не могут быть выполнены, но они выполняются путем введения извне команды, которая создает команды (возможно, процедуры), для которых было выполнено расширение макроса во время их определения.Как только вы работаете таким образом, вы можете делать довольно радикальные перестановки кода;вводимый текст вообще не должен выглядеть как Tcl.
В Wiki Tcler есть несколько страниц на эту тему;http://wiki.tcl.tk/3888, http://wiki.tcl.tk/11156, и т. Д. Эта система макросов, которую я написал и использую вживую, встроена в tclquadcode .Благодаря этому я могу использовать метки базовых блоков псевдосборки как макроподобные вещи: это включает не просто простую замену, а скорее перемещение соответствующего кода в начало тела скрипта, так что мне не нужновручную объявить соответствующие переменные заранее (что я ранее считал ужасно подверженным ошибкам и трудным для чтения);он внутренне использует лямбда-термины, а не процедуры для общего контроля, но здесь разница не так уж важна, учитывая, что макрос label
, а не build
. Вот пример использования этого кода .Соответствующий бит этого кода (если я делаю типичные замены для читабельности):
build {
my condBr [my and [my isNumericInt $x] [my isNumericInt $y]] \
$ints $doubles
label ints:
my ret [my add(INT,INT) [my numeric.int $x] [my numeric.int $y]]
label doubles:
set left [my cast(DOUBLE) $x "left"]
set right [my cast(DOUBLE) $y "right"]
my ret [my add(DOUBLE,DOUBLE) $left $right]
}
Это внутренне переписано довольно примерно, примерно так:
apply {{func x y} {
set ints [$func block "ints"]
set doubles [$func block "doubles"]
my SetCurrentBasicBlock [$func getEntryBlock]
my condBr [my and [my isNumericInt $x] [my isNumericInt $y]] \
$ints $doubles
my SetCurrentBasicBlock $ints
my ret [my add(INT,INT) [my numeric.int $x] [my numeric.int $y]]
my SetCurrentBasicBlock $doubles
set left [my cast(DOUBLE) $x "left"]
set right [my cast(DOUBLE) $y "right"]
my ret [my add(DOUBLE,DOUBLE) $left $right]
}} $func $x $y
Очень приблизительно.Реальная версия сгенерированного кода на lot более сложна из-за отладки отслеживания метаданных;это довольно здоровенная генерация кода под капотом.