Можно ли включить выход при ошибке в интерактивной оболочке Tcl?

Мне нужно автоматизировать огромную интерактивную программу Tcl, используя Tcl expect.

Как я понял, эта территория действительно опасна, так как мне нужно расширить уже существующий массив кода, но я не могу полагаться на ошибки, которые действительно приводят к сбою программы с положительным кодом выхода, как я мог бы в обычном скрипте. Это означает, что я должен думать обо всех возможных вещах, которые могут пойти не так, и «ожидать» этого.

В настоящее время я использую процедуру «die» вместо того, чтобы вызывать ошибку в моем собственном коде, которая автоматически завершается. Но такого рода состояние ошибки не может быть catched, и затрудняет обнаружение ошибок, особенно в коде, написанном не мной, поскольку в конечном итоге большинство библиотечных подпрограмм будут основаны на error.

Поскольку у меня есть доступ к оболочке Tcl программы, можно ли включить функцию отказа при ошибке?

РЕДАКТИРОВАТЬ:

Я использую Tcl 8.3, что является серьезным ограничением с точки зрения доступных инструментов.

Примеры ошибок, при которых я хотел бы автоматически выйти:

% puts $a(2)
can't read "a(2)": no such element in array
while evaluating {puts $a(2)}
%

% blublabla
invalid command name "blublabla"
while evaluating blublabla
% 

Как и любая другая ошибка, из-за которой нормальный скрипт завершается. Они могут подниматься с 10 уровней внутри вызовов процедур.

Я также пробовал переопределить глобальную команду error, но не все ошибки, которые могут возникнуть в Tcl, используют ее. Например, указанная выше ошибка «команда не найдена» не прошла через мою пользовательскую error процедуру.


person Larry    schedule 21.10.2019    source источник
comment
Можете ли вы обновить свой пост, включив в него пример сценария Tcl, который вы хотите автоматизировать. Можете ли вы сделать так, чтобы он показывал, из каких error событий вы хотите выйти?   -  person mrcalvin    schedule 23.10.2019
comment
8.3 - это более чем десятилетний период даже без расширенной поддержки ...   -  person Donal Fellows    schedule 27.10.2019


Ответы (1)


Поскольку у меня есть доступ к оболочке Tcl программы, можно ли включить функцию отказа при ошибке?

Позвольте мне подвести итог своими словами: вы хотите выйти из интерактивной оболочки Tcl в случае ошибки, вместо того, чтобы снова предлагать подсказку?

Обновлять

Я использую Tcl 8.3, что является серьезным ограничением с точки зрения доступных инструментов [...] только исходных патчей для кода C.

Если вам кажется, что вы глубоко в этой кроличьей норе, почему бы не добавить еще один патч для исходного кода?

--- tclMain.c   2002-03-26 03:26:58.000000000 +0100
+++ tclMain.c.mrcalvin  2019-10-23 22:49:14.000000000 +0200
@@ -328,6 +328,7 @@
        Tcl_WriteObj(errChannel, Tcl_GetObjResult(interp));
        Tcl_WriteChars(errChannel, "\n", 1);
        }
+       Tcl_Exit(1);
    } else if (tsdPtr->tty) {
        resultPtr = Tcl_GetObjResult(interp);
        Tcl_GetStringFromObj(resultPtr, &length);

Это не проверено, исходники Tcl 8.3.5 не компилируются для меня. Но этот раздел внутреннего Tcl сопоставим с текущими источниками, протестированными с использованием моей исходной установки Tcl 8.6.

Для записей

Боюсь, что со стандартной оболочкой (tclsh) это немного неудобно. Следующее может сработать для вас (хотя я могу представить себе случаи, когда это могло бы вас подвести). Идея

  • для перехвата записи в stderr (сюда интерактивная оболочка перенаправляет сообщения об ошибках, прежде чем вернуться к приглашению).
  • чтобы различать произвольные записи в stderr и ошибки, можно использовать глобальную переменную ::errorInfo в качестве контрольной.

Шаг 1. Определите перехватчик канала

oo::class create Bouncer {
    method initialize {handle mode} {
        if {$mode ne "write"} {error "can't handle reading"}
        return {finalize initialize write}
    }
    method finalize {handle} {
        # NOOP
    }

    method write {handle bytes} {
        if {[info exists ::errorInfo]} {
        # This is an actual error;
        # 1) Print the message (as usual), but to stdout
        fconfigure stdout -translation binary
        puts stdout $bytes
        # 2) Call on [exit] to quit the Tcl process
        exit 1
    } else {
        # Non-error write to stderr, proceed as usual
            return $bytes
    }
    }
}

Шаг 2. Зарегистрируйте перехватчик для stderr в интерактивных оболочках

if {[info exists ::tcl_interactive]} {
    chan push stderr [Bouncer new]
}

После регистрации ваша интерактивная оболочка будет вести себя так:

% puts stderr "Goes, as usual!"
Goes, as usual!
% error "Bye, bye"
Bye, bye

Некоторые замечания

  • Вы должны быть осторожны с методом Bouncer write, сообщение об ошибке уже было обработано для кодировки символов (следовательно, вызов fconfigure).
  • Вы можете поместить это в пакет Tcl или модуль Tcl, чтобы загрузить вышибалу с помощью package req.
  • Я могу представить, что ваша программа записывает в stderr, а переменная errorInfo установлена ​​(как оставшаяся часть), это вызовет непреднамеренный выход.
person mrcalvin    schedule 22.10.2019
comment
Вы также можете сделать метод write более надежным, добавив try {...} finally {exit 1} в ветку ошибок? - person mrcalvin; 22.10.2019
comment
Спасибо за ответ. К сожалению, ваше 3-е замечание именно так, потому что моя программа, которую нужно автоматизировать, запускается. У меня также нет доступа к oo, но я думаю, что мог бы изобрести альтернативу, основанную на вашей идее обработки канала stderr, если бы это решило мою проблему. - person Larry; 22.10.2019
comment
Какую именно версию Tcl вы используете? - person mrcalvin; 22.10.2019
comment
8.3. Так что я даже не смог попробовать, как вы предложили. Он также имеет некоторые собственные бинарные патчи, созданные людьми, которые давно ушли из компании, что даже приводит к тому, что при возникновении некоторых ошибок печатаются дополнительные материалы. Но не тогда, когда другие ... - person Larry; 22.10.2019
comment
Боюсь, что с версией 8.3 ничего из вышеперечисленного невозможно. Есть шанс на обновление? - person mrcalvin; 23.10.2019
comment
вовсе нет. Да, я просто зашел на страницу документации и увидел, что даже chan недоступен в версии 8.3, так что даже если бы я мог взломать свой путь с помощью oo, это не сработало бы. - person Larry; 23.10.2019
comment
Одно исправление: на самом деле у нас нет двоичных патчей, только исходные патчи для кода C. - person Larry; 23.10.2019
comment
В версии 8.3 (правда ?!) может быть проще определить собственный REPL, чем использовать значение по умолчанию в Tclsh. Это не очень много кода (с info complete в помощь); код должен быть в Tcler Wiki. Причудливые инструменты более поздних версий недоступны в такой старой кодовой базе ... - person Donal Fellows; 27.10.2019