Пароль: Loop_Exit_2010
Вот текст для удобства:
Код:
PROCEDURE comment;
  CONST char=0; eot=1; cbeg=2; cend=3; (* типы csym *)
  VAR ch2: CHAR; ch2hasVal: BOOLEAN; csym: INTEGER;
  
  (* минисканер, меняет: ch, csym, ch2hasVal, состояние R *)
  PROCEDURE getSym;
  BEGIN
    csym:=char;
    IF ch2hasVal THEN
      ch:=ch2; ch2hasVal:=FALSE;
    ELSIF R.eot THEN
      csym:= eot;
    ELSE
      R.ReadChar(ch);
    END;
    IF csym = char THEN
      IF ch= "(" THEN
        IF ~R.eot THEN
          R.ReadChar(ch2);
          IF ch2="*" THEN
            csym:=cbeg;
          ELSE
            ch2hasVal:=TRUE;
          END;
        END;
      ELSIF ch= "*" THEN
        IF ~R.eot THEN
          R.ReadChar(ch2);
          IF ch2=")" THEN
            csym:=cend;
          ELSE
            ch2hasVal:=TRUE;
          END;
        END;
      END;
    END;
  END getSym;
  PROCEDURE com(Level:INTEGER); (* минипарсер *)
  BEGIN
    getSym;
  (* инвариант цикла: от старта комментария до текущей позиции
   * вложенные комментарии обработаны, остальное пропущено *)
    WHILE (csym # eot) & (csym # cend) DO
      IF csym = cbeg THEN
        com(Level+1);
      ELSE
        getSym;
      END;
    END;
    IF csym = cend THEN
      IF Level=0 THEN
        R.ReadChar(ch);
      ELSE
        getSym;
      END;
    ELSE
      Mark("комментарий не завершен");
    END;
  END com;
BEGIN
  ch2hasVal:=FALSE;
  com(0);
END comment;
Т.к. исходные предпосылки, на основании которых делалось решение, оказались неверны, то оно представляет только историческую ценность.
А именно, неверны предположения, что
1. R.eot "заглядывает вперед".
2. При R.eot=истина недопустимо чтение из файла.
Я был зациклен на правильном построении циклов, и вариант с автоматом не рассматривал (а должен был бы!!!).
Суть рассуждений сводится к следующему: грамматику комментариев можно описать так (обозначения те же, что и в программе):
com = cbeg {char|com} cend.
Здесь com – нетерминальный символ, остальные терминальные.
Т.к. комментарии нужно пропускать, получается "язык в языке".
Для языка комментариев определен свой минипарсер и минисканер. Для разбора используется метод рекурсивного спуска.
Т.е., весь огород городился вот для этого цикла:
Код:
WHILE (csym # eot) & (csym # cend) DO
  IF csym = cbeg THEN com; ELSE getSym; END;
END;
Сейчас вижу, что лучше бы записать так:
Код:
WHILE (csym = char) OR (csym = cbeg) DO
  IF csym = cbeg THEN com; ELSE getSym; END;
END;
Готовый кандидат на цикл Дейкстры.
Переменная Level там на фиг не нужна. Однако, пришлось ее ввести, чтобы корректно переключиться с минисканера, который заглядывает вперед на один символ, на обычное посимвольное чтение. Вот этот кусок мне больше всего не нравится.
---------------------------
Вывод: для пропуска комментариев следует использовать конечный автомат, как это сделал Евгений.