Пароль: 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 там на фиг не нужна. Однако, пришлось ее ввести, чтобы корректно переключиться с минисканера, который заглядывает вперед на один символ, на обычное посимвольное чтение. Вот этот кусок мне больше всего не нравится.
---------------------------
Вывод: для пропуска комментариев следует использовать конечный автомат, как это сделал Евгений.