Главная страница Выбор входного языка Промежуточное представление Разработка Front-end'а Разработка Back-end'а Пара слов про оптимизацию



Входной язык - это сильно сказано, ибо речь здесь пойдет про компиляцию примитивных арифметически правильных выражений. Для описания синтаксиса будут использоваться Расширенные Бэкуса-Наура Формы (РБНФ)...

Итак...

Выражение - это последовательность слагаемых, разделенных операциями сложения. Перед первым слагаемым может стоять знак :

Выражение = ["+"|"-"] Слагаемое {ОперацияСложения Слагаемое}.

Слагаемое в свою очередь - это последовательность множителей, разделенных операциями умножения:

Слагаемое = Множитель {ОперацияУмножения Множитель}. 

А множитель - это совсем просто: либо перменная, либо число, либо выражение в скобках:

Множитель = Переменная | Число | "(" Выражение ")".

ОперацияСложения - это либо плюс(+), либо минус(-):

ОперацияСложения = "+" | "-".

ОперацияУмножения - это либо умножение(*), либо остаток отделения(%), либо целая часть от деления(/):

ОперацияУмножения = "*" | "%" | "/".

Для простоты переменная - это любая буква латинского алфавита.

Переменная = буква.

Ну и наконец, число - это последовательность цифр.

Число = цифра {цифра}.

Все рассмотренные выше арифметические операции бинарны, за исключением унарного "плюса" и "минуса" перед первым слагаемым. Вот пример привильного арифметического выражения:

-(-a+b%c)*190/(-324*e+a)+b

С синтаксисом покончено... Настало время привести текст лексического анализатора. Но для начала - драйвер исходного текста(только DEFINITION-часть)

Чуть не забыл: все приведенные здесь и далее исходные тексты написаны на языке OBERON II.

(*Драйвер исходного текста*)
(*          (C)2004,pm_kas*)
DEFINITION Text;
CONST
   chEOT*     = 0FFX; (*символ конец текста*)
VAR
   Ch*       : CHAR;  (*текущий символ     *)

   PROCEDURE NextCh*;  
(*Возвращает следующий символ в переменную Ch*)

   PROCEDURE ResetText*;
(*"Сбрасывает" поток символов*)

   PROCEDURE CloseText*;
(*Закрывает поток символов*)

END Text.

Думаю коментарии излишни :)
Ниже приведен исходник сканера (лексического анализатора).

(* Лексический анализатор *)
(*       (C)2004, pm_kas  *)
MODULE Scan;
IMPORT
   Text, Error;
CONST

   lexNum*  =01; (*число                             *)
   lexName* =02; (*переменная                        *)

   lexPlus* =03; (*операция "плюс"                   *) 
   lexMinus*=04; (*операция "минус"                  *)
  
   lexMult* =05; (*операция "умножить"               *) 
   lexMOD*  =06; (*операция "остаток от деления"     *) 
   lexDIV*  =07; (*операция "целая часть от деления" *) 

   lexLPar* =17; (*левая скобка                      *) 
   lexPPar* =18; (*правая скобка                     *) 

   lexEOT*  =65; (*конец текста                      *)

TYPE
   tName*   = CHAR;
   tLex* = INTEGER; 
VAR 
   Lex*     : tLex;      (*текущая лексема*)
   Num*     : LONGINT;   
   Name*    : tName;
(***** процедура разбора числа *************************)
   PROCEDURE Number*;
   VAR
      d  :  LONGINT;
   BEGIN
      Lex:=lexNum;
      Num:=0;
      WHILE ('0'<=Text.Ch)&(Text.Ch<='9') DO
         d:=ORD(Text.Ch)-ORD('0');
         IF (MAX(INTEGER)-d) DIV 10 >= Num THEN
            Num:=10*Num+d
         ELSE
            Error.Error('Слишком большое число');
         END;
         Text.NextCh;
      END;
   END Number;

(***** выдать следующую лексему **********************)
   PROCEDURE NextLex*;
   BEGIN
      WHILE Text.Ch <= ' ' DO
         Text.NextCh;
      END;
      CASE Text.Ch OF
         Text.chEOT        : Lex:=lexEOT;
      |  'A'..'Z','a'..'z' : Lex:=lexName;
                             Name:=Text.Ch;
                              Text.NextCh;
      |  '0'..'9'          : Number;
      |  '+'               : Lex:=lexPlus;
                              Text.NextCh;
      |  '-'               : Lex:=lexMinus;  
                              Text.NextCh;
      |  '*'               : Lex:=lexMult;
                              Text.NextCh;
      |  '%'               : Lex:=lexMOD;
                              Text.NextCh;
      |  '/'               : Lex:=lexDIV;
                              Text.NextCh;
      |  '('               : Lex:=lexLPar;
                              Text.NextCh;
      |  ')'               : Lex:=lexPPar;  
                              Text.NextCh;
      ELSE
         Error.Error('Недопустимый символ')
      END;
   END NextLex;

(**** инициализировать сканер ***********************)
   PROCEDURE InitScan*;
   BEGIN
      NextLex;
   END InitScan;
END Scan.

Приведенный выше текст прост для понимания, поэтому остается лишь привести DEFINITION-часть модуля Error

(*Модуль обработки ошибок*)
(*         (C)2004,pm_kas*)
DEFINITION Error;

   PROCEDURE Error*(Mess : ARRAY OF CHAR);
(*Выдать соообшение об ошибке Mess*)

   PROCEDURE Expected*(Mess : ARRAY OF CHAR);
(*Выдать соообшение об ошибке вида: Ожидается Mess*)

END Error.
(C) 2004, PM_KAS