if (Р <= Length(S)) and IsSign(S[P]) then Inc(P);
if (P > Length(S)) or not IsDigit(S[P]) then
raise ESyntaxError.Create(
'Ожидается цифра в позиции ' + IntToStr(Р));
repeat
Inc(P);
until (P > Length(S)) or not IsDigit(S[P]);
if (P <= Length(S)) and IsSeparator(S[P]) then begin
Inc(P);
if (P > Length(S)) or not IsDigit(S[P]) then
raise ESyntaxError.Create(
'Ожидается цифра в позиции ' + IntToStr(Р));
repeat
Inc(P);
until (P > Length(S)) or not IsDigit(S[P]);
end;
if (P <= Length(S)) and IsExponent(S[P]) then
begin
Inc(P);
if Р > Length(S) then
raise ESyntaxError.Create('Неожиданный конец строки');
if IsSign(S[P]) then Inc(P);
if (P > Length(S)) or not IsDigit(S[P]) then
raise ESyntaxError.Create(
'Ожидается цифра в позиции ' + IntToStr(Р));
repeat
Inc(P);
until (P > Length(S)) or not IsDigit(S[P]);
end;
Result:= StrToFloat(Copy(S, InitPos, P — InitPos));
end;
// Проверка символа на соответствие
function IsOperator(Ch: Char): Boolean;
begin
Result:= Ch in ['+', '-', '*', '/'];
end;
// Проверка строки на соответствие
// и вычисление выражения
function Expr(const S: string): Extended;
var
P: Integer;
OpSymb: Char;
begin
P:= 1;
Result:= Number(S, P);
while (P <= Length(S)) and IsOperator(S[P]) do
begin
OpSymb:= S[P];
Inc(P);
case OpSymb of
'+': Result:= Result + Number(S, P);
'-': Result:= Result — Number(S, P);
'*': Result:= Result * Number(S, P);
'/': Result:= Result / Number(S, P);
end;
end;
if P <= Length(S) then
raise ESyntaxError.Create(
'Heкорректный символ в позиции ' + IntToStr(Р));
end;
Код приведен практически без комментариев, т. к. он очень простой, и все моменты, заслуживающие упоминания, мы уже разобрали в тексте. На прилагаемом компакт-диске находится программа SimpleCalcSample, которая демонстрирует работу нашего калькулятора. Калькулятор выполняет действия над числами слева направо, без учета приоритета операций, т. е. вычисление выражения "2+2*2" даст 8.
Грамматика выражения является простой для разбора, т. к. разбор выражения идет слева направо, и для соотнесения очередной части строки с тем или иным нетерминальным символом на любом этапе анализа достаточно знать только следующий символ. Такие грамматики называются LR(1) — грамматиками (в более общем случае требуется не один символ, а одна лексема). Класс этих грамматик был исследован Кнутом.
Грамматика Паскаля не относится к классу LR(1) — грамматик из-за уже упоминавшейся проблемы отнесения else
к тому или иному if
. Чтобы решить эту проблему, приходится вводить два нетерминальных символа — завершенной формы оператора if
(с else
) и незавершенной (без else
). Таким образом, встретив в тексте программы лексему if
, синтаксический анализатор не может сразу отнести ее к одному из этих символов, пока не продвинется вперед и не натолкнется на наличие или отсутствие else
. А поскольку оператор if
может быть оператором в циклах for
, while
или в операторе with
, для них также приходится вводить завершенную и незавершенную форму. Именно из-за этой проблемы Вирт (разработчик Паскаля) отказался от идеи составного оператора и модифицировал синтаксис в своем новом языке Оберон таким образом, чтобы проблема else
не возникала.