Добавление переменных к синтаксическому анализатору
Все языки программирования и многие калькуляторы используют
переменные для запоминания значений, которые потребуются позднее.
Простой синтаксический анализатор из предыдущего раздела должен
быть расширен обработкой переменных прежде, чем его можно будет
использовать для этой цели.
Во-первых, вам нужны собственно переменные. Так как синтак-
сический анализатор ограничен использованием только целых выраже-
ний, то вы также можете использовать переменные только целого ти-
па. Синтаксический анализатор будет распознавать только
переменные от А до Z, хотя вы могли бы избавиться от этого огра-
ничения, если бы захотели. Каждая переменная использует ячейку
матрицы из 26 элементов. Следовательно, нам нужно добавить
varsi array[0..25] of real; { 26 переменных }
Однако, перед тем, как начать использовать эти переменные,
вам следует установить их в 0.
Вам также нужна процедура для нахождения значения данной пе-
ременной. Так как вы используете буквы от А до Z в качестве имен
переменных, вы можете легко индексировать по матрице vars на ос-
нове имени переменной. Далее представлена функция FindVar:
function FinVar(s: str80): real;
var
t: integer;
begin
FinVar:=vars[ord(Upcase(s[1]))-ord('A')];
end; {FindVar}
Как вы видите, данная функция может принимать имена переменных
любой длины, но значащей в имени является только первая буква. Вы
можете модифицировать данную функцию, чтобы она удовлетворяла ва-
шим нуждам.
Вы должны также модифицировать функция primitive, чтобы она
трактовала и числа и переменные, как примитив, следующим образом
procedure Primitive;
begin
if TokType=NUMBER then
val(token, result, code)
else if TokType=VARIABLE then
result:=FindVar(token)
else
Serror(1);
GetToken;
end.
Все это необходимо, чтобы синтаксический анализатор мог пра-
вильно использовать переменные; однако, отсутствует способ прис-
ваивания значений переменным. Вы можете назначить значения пере-
менным вне синтаксического анализатора, но, так как можно
трактовать=, как оператор присваивания, вы имеете возможность
сделать его частью синтаксического анализатора различными метода-
ми. Один из них состоит в том, чтобы добавить level1 к синтакси-
ческому анализатору, как показано далее:
{ процесс предложения присваивания }
procedure Level1;
var
hold: real;
temp: Type;
slot: integer;
TempToken: str80;
begin
if TokType=VARIABLE then
begin
{сохранить старую лексему}
TempToken:=token;
temp:=TokType;
slot:=ord(Upcase(token[1]))-ord)'A');
GetToken; {проверить,существует ли = для присваивания }
if token[1]<>'"' then
begin
PutBack; {заменить лексему }
{восстановить старую лексему}
token := TempToken;
TokType := temp;
Level2(result);
end else
begin
GetToken;
Level2(result);
vars[slot] := result;
end;
end {if}
else Level2(result);
end; {Level1}
Когда переменная встречается как первая лексема в выражении,
она может быть либо целью выражения, как в
А=В*10
либо просто частью выражения
А-123
В level1,
чтобы знать, что из себя представляет переменная, должен выпол-
няться просмотр вперед. Просмотр вперед - это процесс, сохраняю-
щий текущую лексему и затем получающий следующую для анализа. В
данном случае, если существующая лексема - это =, то вы знаете,
что будет выполняться присваивание, и выполняются соответствующие
процедуры. Если это не =, то лексема будет возвращена назад в
строку выражения, а предыдущая лексема должна быть восстановлена.
Вы можете сделать это с помощью процедуры Putbacl, которая просто
уменьшает индекс t. Как вы можете видеть, "просмотр вперед" тре-
бует определенного времени и его следует в общем случае избегать,
исключая случаи, когда он абсолютно необходим.
Далее представлены целиком расширенный синтаксический анали-
затор, вспомогательные функции и главная программа:
{данная программа демонстрирует синтаксический анализатор
который допускает применение переменных }
program parser2;
type
str80 = string[80];
TType = (DELIMITER, VARIABLE, NUMBER);
var
token, prog: str80;
TokType: TType;
code, t: integer;
result: real;
vars: array[0..25] of real; {26 переменных}
{данная функция возвращает TRUE, если ch является буквой
алфавита}
function IsAlpha(ch: char): boolean;
begin
IsAlpha:= (UpCase(ch)>='A') and (UpCase(ch)<='Z');
end; {IsAlpha}
{данная функция возвращает TRUE, если ch является
символом новой строки, табуляции или пробелом }
function IsWhite(ch: char): boolean;
begin
IsWhite: = (ch=' ') or (ch=chr(9)) or (ch=chr(13));
end; {IsWhite}
{данная функция возвращает TRUE, если ch является
разделителем}
function IsDelim(ch: char): boolean;
begin
if pos(ch, ' +-/*%^=()S')<>0 then IsDelim: = TRUE
end; {IsDelim}
{данная функция возвращает TRUE, если ch - цифра от 0 до 9}
function IsDigit(ch: char): boolean;
begin
IsDigit: = (ch>='0') and (ch<='9');
end; {IsDigit}
{GotToken считывает следующую лексему из входного потока}
procedure GetToken;
var
temp: str80;
begin
token: = ''; {пустая строка }
while(IsWhite(prog[t])) do t:=t+1; {пропустить
предшествующие пробелы}
if prog[t]='S' then token: = 'S';
if pos(prog[t], '+-*/%^=()')<>0 then
begin
TokType: = DELIMITER;
token: = prog[t]; {является оператором }
t: = t+1;
end else if IsAlpha(prog[t]) then
begin
While(not IsDelim(prog[t])) do
begin
token: = concat(token, prog[t]); { построить лексемы }
t: = t+1;
end;
TokType: = VARIABLE;
end
else if IsDigit(prog[t]) then
begin
while(not IsDelim[t])) do
begin
token: = concat(token,prog[t]); { построить число }
t: = t+1;
TokType: = NUMBER;
end;
end;
end; {GetToken}
{ PutBack возвращает лексему во входной поток }
procedure PutBack;
begin
t := t-length(token);
end; {PutBack}
{отображение сообщений об ошибках }
Procedure Serror(i: integer);
begin
case i of
1: WriteLn('синтаксическая ошибка ');
2: WriteLn('несбалансированные скобки');
3: WriteLn('выражение отсутствует ');
end;
end; {Serror}
{возведение в степень }
function Pwr(a, b: real): real;
var
t: integer;
temp: real;
begin
if a=0 then Pwr: = 1 else
begin
temp: = a;
for t: = trunc(b) cownto 2 do a: = a*temp;
Pwr: = a;
end;
end;
{данная функция выполняет заданные арифметические операции}
procedure Arith(op: char; var result, operand: real);
begin
case op of
'+': result: = result+operand;
'-': result: = result-operand;
'*': result: = result*operand;
'/': result: = result/operand;
'^': result: = result^operand;
end;
end; {Arith}
{FindVar возвращает значение переменной}
function FindVar(s: str80): real;
var
t: integer;
begin
FindVar:=vars[ord(Upcase(s[1]))-ord('A')];
end; {FindVar}
{********** синтаксический анализатор выражений *************}
{**** with variables and assignment *******}
procedure Level2(var result: real); forward;
procedure Level1(var result: real); forward;
procedure Level3(var result: real); forward;
procedure Level4(var result: real); forward;
procedure Level5(var result: real); forward;
procedure Level6(var result: real); forward;
procedure Primitive(var result: real); forward;
{это точка входа в синтаксический анализатор }
procedure GetExp(var result: real);
begin
GetToken;
if Length(token)<>0 then
Level1(result)
else
Serror(3);
end; {GetExp}
{ процесс предложения присваивания }
procedure Level1;
var
hold: real;
temp: Type;
slot: integer;
TempToken: str80;
begin
if TokType=VARIABLE then
begin
{сохранить старую лексему}
TempToken:=token;
temp:=TokType;
slot:=ord(Upcase(token[1]))-ord)'A');
GetToken; {проверить,существует ли = для присваивания }
if token[1]<>'"' then
begin
PutBack; {заменить лексему }
{восстановить старую лексему}
token := TempToken;
TokType := temp;
Level2(result);
end else
begin
GetToken;
Level2(result);
vars[slot] := result;
end;
end {if}
else Level2(result);
end; {Level1}
{процесс + или - }
procedure Level2;
var
op: char;
hold: real;
begin
Level3(result);
op := token[1];
while(op='+') or (op='-') do
begin
GetToken;
Level3(hold);
arith(op, result, hold);
op := token[1]
end;
end; {Level2}
{процесс * или \ }
procedure Level3;
var
op: char;
hold: real;
begin
Level4(result);
op := token[1];
while ((op='*') or (op='/')) do
begin
GetToken;
Level4(hold);
arith(op, result, hold);
op := token[1];
end;
end; {Level3}
{процесс ^ (возведение в степень)}
procedure Level4;
var
hold: real;
begin
Level5(result);
if token[1]='^' then
begin
GetToken;
Level4(hold);
arith('^', result, hold);
end;
end; {Level4}
{процесс унарного оператора}
procedure Level5;
var
op: char;
begin
op := ' ';
if ((TokType=DELIMITER) and ((token[1]='+') or
(token[1]='-')))
then begin
op := token[1];
GetToken;
end;
Level6(result);
if op='-' then result := -result;
end; {Level5}
{процесс скобок }
procedure Level6;
begin
if(token[1]='(') and (TokType=DELIMITER) then
begin {заключенное в скобки выражение}
GetToken;
Level2(result);
if token[1]<>')' then Serror(2); {скобки не сбалансированы}
GetToken;
end
else Primitive(result);
end; {Level6}
procedure Primitive;
begin
if TokType=NUMBER then
val(token, result, code)
else if TokType=VARIABLE then
result := FindVar(token)
else
Serror(1);
GetToken;
end;
begin {главная}
for t:=0 to 25 do vars[t]:=0; {инициализировать
переменные}
repeat
t := 1;
Write('Введите выражение: '); 38
ReadLn(prog);
prog := concat(prog, '$');
if(prog<>'quit$') then
begin
GetExp(result);
writeLn(result);
end;
until prog='quit$';
end.
Теперь при расширенном синтаксическом анализаторе вы можете
ввести такие выражения, как
А=10/4
А-В
С=А*(Р-21)
и они будут вычислены правильно.