Обрабатывать аргумент как строку в выражении Tcl 'expr'

Я использую короткий синтаксис оператора if, например:

proc WriteParameter {Parameter Value} {
    # Ugly option - WORKS
    if {$Parameter eq "Unique"} {
        set Register ControlStatus
        set Data ${Value}0
        set Mask EF
    } else {
        set Register $Parameter
        set Data $Value
        set Mask {}
    }

    # Elegant option - DOESN'T WORK
    set Register [expr {$Parameter eq "Unique" ? "ControlStatus" : $Parameter}]
    set Data [expr {$Parameter eq "Unique" ? ${Value}0 : $Value}]
    set Mask [expr {$Parameter eq "Unique" ? EF : {}}]

    puts $Register-$Data-$Mask
    return 0
}
set Value 4E20 ;# Merely hexadecimal number
set Parameter Regular
WriteParameter $Parameter $Value

Проблема заключается в том, что в элегантном варианте, поскольку оператор 'expr' всегда обрабатывает свои аргументы как целые числа, 'Data' получает значение 4e+20, которое является просто научной нотацией 'Value'.

Однако мне нужно, чтобы "Данные" были "Значение" (например, для записи во внешний регистр).

Есть идеи?

1 ответ

Решение

expr язык довольно отличается от остальной части Tcl. Вы должны больше заботиться о синтаксисе там. Положить вещи в "двойные кавычки" может помочь.

set Register [expr {$Parameter eq "Unique" ? "ControlStatus" : $Parameter}]
set Data [expr {$Parameter eq "Unique" ? "${Value}0" : $Value}]
set Mask [expr {$Parameter eq "Unique" ? "EF" : {}}]

Тем не менее, вы могли бы лучше использовать if поскольку это не пытается преобразовать результаты его рук в число (используя приблизительно правила для констант C) в отличие от expr,

set Register [if {$Parameter eq "Unique"} {string cat ControlStatus} {string cat $Parameter}]
set Data [if {$Parameter eq "Unique"} {string cat $Value 0} {string cat $Value}]
set Mask [if {$Parameter eq "Unique"} {string cat EF}]
# We can omit the else clause; the default is an empty string anyway

Это зависит от string cat, который был представлен в Tcl 8.6.3 (ну, 8.6.2, но в подсистеме ввода / вывода были некоторые ошибки, которых вы действительно хотите избежать). Если вы используете что-то от 8.5 до 8.6.1, используйте это вместо:

set Register [if {$Parameter eq "Unique"} {return -level 0 ControlStatus} {return -level 0 $Parameter}]
set Data [if {$Parameter eq "Unique"} {return -level 0 ${Value}0} {return -level 0 $Value}]
set Mask [if {$Parameter eq "Unique"} {return -level 0 EF}]

Да, return -level 0 просто дает значение в качестве результата. "Очевидное и обнаруживаемое"…

Если вы используете 8.4 или более раннюю версию (Обновите, мужик! У вас нет поддержки безопасности!), Вам нужна небольшая вспомогательная процедура:

proc value {value} {return $value}
set Register [if {$Parameter eq "Unique"} {value ControlStatus} {value $Parameter}]
set Data [if {$Parameter eq "Unique"} {value $Value 0} {value $Value}]
set Mask [if {$Parameter eq "Unique"} {value EF}]

value описанная выше процедура будет работать и с более поздними версиями Tcl, но она дает менее эффективный байт-код.


Тем не менее, для всего вышеперечисленного я бы сделал что-то другое:

set Register $Parameter
set Data $Value
set Mask ""
if {$Parameter eq "Unique"} {
    set Register ControlStatus
    append Data 0
    set Mask EF
}

Я мог бы также использовать scan проанализировать значение и format составить результаты обратно в гекс (при необходимости). Таким образом, было бы намного проще думать о ценности, а не только о представлении.

Другие вопросы по тегам