Страница 36 из 37
Добавление переменных к синтаксическому анализатору Все языки программирования и многие калькуляторы используют переменные для запоминания значений, которые потребуются позднее. Простой синтаксический анализатор из предыдущего раздела должен быть расширен обработкой переменных прежде, чем его можно будет использовать для этой цели. Во-первых, вам нужны собственно переменные. Так как синтак- сический анализатор ограничен использованием только целых выраже- ний, то вы также можете использовать переменные только целого ти- па. Синтаксический анализатор будет распознавать только переменные от А до 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) и они будут вычислены правильно.
|