Как доказать, что предварительное условие процедуры SPARK.Text_IO будет выполнено

Я использую SPARK.Text_IO из примера spark_io в SPARK Discovery 2017.

Моя проблема в том, что многие из процедур SPARK.Text_IO имеют предварительное условие, что я не знаю, как начать пытаться доказать именно то, что стандартный ввод читаем, а мы не в конце файла. Моя попытка, как показано в приведенном ниже коде, состояла в том, чтобы добавить предварительное условие процедуры SPARK.Text_IO (в данном случае Get_Immediate) к предварительному условию вызывающей процедуры, полагая, что, возможно, это гарантирует доказателю, что это предварительное условие будет истинным, Это не сработало. Вот пример того, о чем я говорю:

Тестовая спецификация:

with SPARK.Ada.Text_IO; use SPARK.Ada.Text_IO;

package test with SPARK_Mode
is

   continue_messages_key : Character := ' ';

   procedure User_Wait_For_Continue_Messages_Key
   with Global => (In_Out => Standard_Input,
                   Input => continue_messages_key),
        Pre => Is_Readable (Standard_Input) and then
                    not End_Of_File; 

end test;

Тестовое тело:

pragma SPARK_Mode(On);

package body test is

   procedure User_Wait_For_Continue_Messages_Key
   is
      IR : Immediate_Result;
      Avail : Boolean;
   begin
      loop
         Get_Immediate(IR, Avail);
         if Avail then
            if IR.Status = Success then
               if IR.Available = True then
                  if IR.Item = continue_messages_key then
                     return;
                  end if;
               end if;
            end if;
         end if;
      end loop;
   end User_Wait_For_Continue_Messages_Key;

end test;

Ошибка, которую дает проверяющий, находится в строке Get_Immediate "средний: предварительное условие может завершиться с ошибкой". Прототип и контракт процедуры Get_Immediate приведены ниже:

procedure Get_Immediate (Item      :    out Character_Result)
     with Global => (In_Out => Standard_Input),
          Pre    => Is_Readable (Standard_Input) and then
                    not End_Of_File,
          Post   => Is_Readable (Standard_Input) and
                    Name (Standard_Input) = Name (Standard_Input)'Old and
                    Form (Standard_Input) = Form (Standard_Input)'Old and
                    Is_Standard_Input (Standard_Input);

Как вы докажете SPARK, что Standard_Input доступен для чтения и что это не конец файла?

1 ответ

Решение

Позвольте мне начать с того, что я не использовал SPARK со времени специальных комментариев, поэтому мой ответ может не отражать текущее использование.

Один из способов взглянуть на SPARK состоит в том, что он заставляет вас думать обо всем, с чем может столкнуться ваша программа. Что должна делать ваша программа, если предварительным условием является False? Вы должны показать, что вы рассматривали эту возможность.

Предполагая, что все операции SPARK.Ada.Text_IO для Standard_Input имеют постусловие, включая Is_Readable (Standard_Input) как это делает Get_Immediate, тогда достаточно поставить что-то вроде

pragma Assert (Is_Readable (Standard_Input) );

в начале вашей программы и добавьте это в постусловие вашей процедуры (и любые другие подпрограммы, которые у вас есть, которые читают Standard_Input). Тогда вы должны быть уверены, что эта часть предварительного условия выполняется во всей вашей программе. (Утверждение может не понадобиться, если SPARK изначально гарантирует, что Standard_Input доступен для чтения.)

not End_Of_File немного сложнее. Возможно, это будет False, по крайней мере, на некоторых платформах. Linux, например, обрабатывает Ctrl-D как EOF, когда вводится первым в строке. Также есть случаи, когда вы читаете из канала или перенаправления ввода. Ваша процедура может вызвать Get_Immediate, когда End_Of_File равен True, если пользователь вводит EOF во время цикла, поэтому неудивительно, что SPARK не может доказать обратное. Вероятно, вам нужно снять эту часть с предварительных условий вашей процедуры и изменить свое тело на что-то вроде

All_Chars : loop
   if not End_Of_File then
      Get_Immediate (IR, Avail);

      exit All_Chars when Avail               and then
                          IR.Status = Success and then
                          IR.Available        and then
                          IR.Item = Continue_Messages_Key;
   end if;
end loop All_Chars;

Тогда вы будете уверены, что предварительное условие Get_Immediate выполнено и будет иметь желаемое поведение (бесконечный цикл), если End_Of_File станет True (при условии, что какое-то поле IR не выполнит одну из ваших проверок в этом случае).

В вашем коде есть некоторые загадочные вещи. Во-первых, это глобальная переменная. Глобальные переменные являются корнем зла или, по крайней мере, гарантируют нечитаемый код. Тогда есть специфика процедуры. Не было бы что-то более общее, как

procedure Wait_For_Key (Key : in Character);

быть таким же простым, чтобы написать и доказать, и более полезным? Тогда есть строка вложенных операторов if, которые мне труднее читать, чем эквивалентные условия, связанные с and then, Наконец, есть сравнение логического значения с истинным. Поскольку "=" возвращает логическое значение, не означает ли это, что для этого требуется логическое значение, то есть то, что уже находится в левой части "="?

Возможно, это означает, что результат "=" также следует сравнивать с True. Тогда результат этого "=" также должен сравниваться с Истиной. Это может быть лучше, так как это гарантирует, что тот, кто пишет, никогда не закончит.

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