Энциклопедия Turbo Pascal. Главы 5-8
Страница 36. Добавление переменных к синтаксическому анализатору


Добавление переменных к синтаксическому анализатору

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

 
« Предыдущая статья   Следующая статья »