Генератор
Проектов

Проект calc

Описание

Проект calc демонстрирует возможности синтаксического разбора.

Задача

Разработка утилиты калькулятора, на вход которого подается выражение, на выходе - вычисленное значение выыражения.

Преимущества решения

Использование системного пакета синтаксического разбора tlex позволяет решить задачу разбора выражения очень просто и минимальным объемом кода, используя широкие встроенные возможности этого пакета. В данном примере видно, как легко и компакто можно решать вопросы синтаксического разбора.

Подробнее

В проекте задействован системный пакет tlex - лексический разбор в строке. Синтаксический разбор производится методом рекурсивного спуска. Каждому нетерминальному символу грамматики соответствует процедура в программе разбора.

Грамматика должна быть приведена к такому виду, чтобы по результатам выяснения, какой терминальный символ расположен в позиции чтения, можно было принять решение, какое правило грамматики применить для разбора. Приведение к такому виду для большинства существующих языков не составляет труда. Для этого достаточно определить надъязык, допускающий некорректные с точки зрения исходной грамматики тексты. В дальнейшем на основе построенного дерева разбора (в том или ином виде) можно проверить корректность.

По сути дела это в реальных трансляторах производится всегда, так как реальные программы не описываются контекстно-свободными грамматиками. КС-грамматика вряд ли может описать язык, в котором запрещено использование самых разных объектов (типов, процедур, переменных) без их описания. Так что такого рода проверки делаются в любом случае.

В нашем случае разбор выполняется в утилите calc для входной строки-параметра вызова программы в консоли. Если не задан ровно один параметр, то выдается сообщение об ошибке. Если параметр задан, то производится его синтаксический анализ с вычислением результата. При успешном анализе в консоли печатается результат вычисления. При ошибке выдается диагностическое сообщение с информацией, в какой позиции текста обнаружена ошибка.

Грамматика на словах. Аддитивное выражение - это последовательность мультипликативных выражений, разделенных знаками операций + или -. Мультипликативное выражение - последовательность множителей, разделенных знаками операций * или /. Множитель - аддитивное выражение в круглых скобках или число.

Аддитивное выражение соответствует процедуре syn_add, мультипликативное выражение - syn_mul, множитель - syn_fact.

Параметры синтаксических процедур:buf - анализируемый текст, xlex - контекст лексического анализа (в том числе позиция чтения), dval - выходной параметр - вычисленное значение.

Используемые лексические процедуры пакета tlex.

Функция tlex.sample - проверяет в текущей позиции наличие предъявленного в параметре образца. Если образец начинается с буквы, то распознаваемый текст должен целиком совпадать с образцом, а не содержать образец в качестве подстроки. Т.е. если мы проверяем наличие образца proc, то текст procdecl в позиции чтения не даст утвердительного результата. При распознавании любой лексемы пропускаются все символы пробелов, концы строк, табуляторы и пр. Пропускаются также комментарии в стиле языка Си и Си++.

Процедура tlex.sample_err - проверяет процедурой tlex.sample наличие образца, при отсутствии инициирует фатальную ошибку с текстом сообщения, содержащим информацию о позиции чтения анализируемой строки.

Такие пары функций/процедур присутствуют в пакете tlex для многих видов лексем. Функция позволяет проверить наличие лексемы, процедура проверяет и при отсутствии инициирует ошибку.

Процедура tlex.double - проверяет в текущей позиции наличие десятичного числа в виде 123.456. Параметр NOSIGN запрещает на уровне лексики знак +-, эти знаки мы распознаем на уровне грамматики. Параметр NOFRAC разрешает числа как с десятичной точкой, так и без нее.

Таким образом, мы имеем синтаксический разбор простых арифметических выражений со скобочной записью.

В нашем примере используется описатель процедуры с ключевым словом procspec (есть funcspec, fprocspec) для реализации косвенной рекурсии. Дело в том, что процедура syn_exp косвенно через вызовы других процедур вызывает саму себя. Поэтому она в начале программы описывается с ключевым словом procspec, а потом повторно описывается уже обычным образом. Это позволяет вызывать процедуру до ее полного описания.

Перечень файлов проекта

gen.bat,gen.bsh - командный файл для первой генерации проекта.

calc.gen - главный файл с описанием компонент проекта. description.txt - описание.

calc.utl - утилита.

Файл calc.gen:

project calc
  /version="01.001"
  /firm="УСТ"
  /http="http://www.ustech.ru"
  /email="managers@ustech.ru"

utility calc

Файл calc.utility:

utility calc:"Calculator"

type t_double : double;
  /frac=6

procspec syn_add(string7 buf,tlex.t_lex xlex,out t_double dval);

proc syn_fact(string7 buf,tlex.t_lex xlex,out t_double dval)
{ dval := 0.0;
  if ( tlex.sample(buf,xlex,"(") )
  { call syn_add(buf,xlex,dval);
    tlex.sample_err(buf,xlex,")");
  }
  else if ( tlex.double(buf,xlex,NOSIGN,NOFRAC,NOEXPON,dval) )
    ;
  else
    tlex.message(buf,xlex,"syntax error");
}

proc syn_mul(string7 buf,tlex.t_lex xlex,out t_double dval)
{ call syn_fact(buf,xlex,dval);

  for ( ; ; )
  { var
      t_double dval1;

    if ( tlex.sample(buf,xlex,"*") )
    { call syn_fact(buf,xlex,dval1);
      dval *= dval1;
    }
    else if ( tlex.sample(buf,xlex,"/") )
    { call syn_fact(buf,xlex,dval1);
      dval /= dval1;
    }
    else
      break;
  }
}

proc syn_add(string7 buf,tlex.t_lex xlex,out t_double dval)
{ var
    bool minus;

  if ( tlex.sample(buf,xlex,"+") )
    ;
  else if ( tlex.sample(buf,xlex,"-") )
    minus := true;

  call syn_mul(buf,xlex,dval);
  if ( minus )
    dval := -dval;

  for ( ; ; )
  { var
      t_double dval1;

    if ( tlex.sample(buf,xlex,"+") )
    { call syn_mul(buf,xlex,dval1);
      dval += dval1;
    }
    else if ( tlex.sample(buf,xlex,"-") )
    { call syn_mul(buf,xlex,dval1);
      dval -= dval1;
    }
    else
      break;
  }
}

proc syn(string7 buf,tlex.t_lex xlex,out t_double dval)
{ call syn_add(buf,xlex,dval);
  tlex.eof_err(buf,xlex);
}

main
{ if ( utl.argc(xutl.yutl) <> 2 )
    error U"Не задан параметр";

  var
    tlex.t_lex xlex,
    t_double dval;

  call syn(utl.argv7(xutl.yutl,1),xlex,dval);
  dprint("result=",dval,"\n");
}
Скачать проект calc