Germany | Finland | Saint Petersburg | Drive

Локальные переменные в QPILE

Опубликовано в M4

Как известно, в QPILE все переменные глобальные. Это упрощает процесс интерпретации/выполнения скрипта, однако создаёт потенциальные опасности для программиста.

 В чем состоят опасности?

Наиболее часто встречающаяся ситуация.

for i from 1 to 10
    f()
end for

Ничего страшного, 10 раз вызывается функция f. Ничего страшного до тех пор, пока внутри этой функции f не переопределяется переменная i.

funс f()
   for i = 100 to 200
      a = i
   end for
end func

 

Вот и оказывается, что при первом же вызове функции f значение переменной i будет перезаписано и исполнение цикла от 1 до 10 прервётся досрочно. В наши планы это никак не входит.

Хорошо, если оба эти фрагмента кода лежат в одном файле и файл небольшой по размерам - тогда есть шанс обнаружить проблему глазами. Если же f - библиотечная функция и написана сто лет назад, то найти ошибку можно будет только путём отладки (брррр......)

Каков может быть выход? Первая идея, что приходит на ум (а ей я в результате и пользуюсь), состоит в следующем. Сам язык qpile нам не предоставляет механизма видимости переменных. Что бы мы с этими переменными ни делали, они несмотря ни на что все равно будут глобальными. Остаётся единственный путь, позволяющий решить нашу проблему. Пусть все переменные остаются глобальными, главное, чтобы они никогда и нигде не пересекались по именам. Как бы не называлась локальная переменная изначально, она должна быть переименована в новое уникальное имя.  В этом случае можно быть уверенным, что никакая переменная в вызывающей процедуре не будет испорчена вызываемой процедурой.

Как добиться? Добиться несложно.

Используем препроцессор. Вводим оператор Local. Определяем его как макрос. 

define(`loc$var$n',0)
define(`Local',foreach(`Y',`define(`loc$var$n',incr(indir(`loc$var$n')))define(Y,`_'indir(`loc$var$n'))',$*))

Первая строка - это инициализация счетчика локальных переменных, вторая - сам макрос. Теперь все имена переменных после упоминания их имени в операторе Local будут заменены на уникальные. Уникальность достигается за счет сквозной нумерации таких переменных во всем портфеле.  Пишем тест:

a = 5
b = 45 / a
c = 90 + b
x = f(a,b,c) func f(a1,b1,c1)
Local(a,b,c)
a = 10
b = a + a1 * b1
c = c1 - 23
result = a+b+c
end func

Как видим, в главном модуле переменным a, b и с присвоены значения. Далее происходит вызов функции f(), внутри которой также используются переменные с этими же именами. При отсутствии макроопределения Local значения переменных a, b и с были бы испорчены. Однако после прохода препроцессором получаем следующий код на qpile: 

 

a = 5
b = 45 / a
c = 90 + b
x = f(a,b,c)
func f(a1,b1,c1)
   _1 = 10
   _2 = _1 + a1 * b1
   _3 = c1 - 23
   result = _1+_2+_3
end func

Теперь вместо переменных a, b и с в вызываемой функции - переменные с именами _1, _2 и _3. Макрос Local для каждой переменной, которая указана в его списке, генерирует новое уникальное имя.

Можно более не заботиться о пересечении имен переменных. Если нам нужна глобальная переменная - все остаётся как и раньше - просто не указываем ее в операторе Local.

Подход крайне прост, однако, ему присущ один недостаток. Действие переопределения имени переменной распространяется не только на текущую функцию, но и дальше, до следующего Local с тем же именем переменной или вплоть до конца файла. Не всегда это удобно. Можно этот недостаток убрать, если по end func уничтожать переопределения, но пока (мне) нет желания это делать. Ну и очевидно, что делать локальной зарезервированную переменную result без веских на то причин не стоит Подмигиваю. Можно запретить объявление result как локальной переменной:

define(`local$var$num',0)
define(`local$prefix',`__')
define(`local$suffix',`')
define(`m4_is_result',`ifelse(m4_toupper(`$1'),RESULT,True,False)')
define(`m4_toupper',`m4_translit(`$1',`a-z',`A-Z')')
define(`Local',`foreach(`Y',`ifelse(m4_is_result(Y),False,`define(`local$var$num',incr(indir(`local$var$num'))) `'define(Y,indir(`local$prefix')`'indir(`local$var$num')`'indir(`local$suffix'))')',$*)')

См. также

Добавить комментарий


Библиотека