Есть ли в TCL такой макрос, как LISP? - PullRequest
0 голосов
/ 31 мая 2018

В LISP можно определить макрос, который расширяется на том же уровне стека, а не добавляется в стек.В TCL можно вызвать процедуру, а затем использовать uplevel 1.Я видел, что это вызывает в некоторых случаях значительное замедление.То, что я хотел бы, это макроподобное расширение.Это доступно в TCL?Я не смог найти ничего, что могло бы предположить, что это так.

1 Ответ

0 голосов
/ 01 июня 2018

Макросы используются не так часто, поскольку 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 более сложна из-за отладки отслеживания метаданных;это довольно здоровенная генерация кода под капотом.

...