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

Проект fcalc

Описание

Проект fcalc демонстрирует возможности синтаксического разбора простейшего языка в файле. Это расширение проекта calc.

Задача

Разработка утилиты функционального калькулятора, на вход которой подается файл с описанием выражений для переменных (имя файла передается утилите как параметр), на выходе - вычисленные значения переменных. Грамматика распознаваемого языка - последовательность операторов вида set <переменная> := <выражение>;

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

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

Подробнее

В проекте задействован системный пакет flex - лексический разбор в файле. Программа представляет собой доработанный вариант из проекта calc.

Грамматика распознаваемого языка - последовательность операторов вида set <переменная> := <выражение>; где выражение как в проекте calc, только дополненное возможностью использовать не только константы, но и переменные.

Для хранения переменных используется документ-очередь env. В нем определена одна запись var, в которой хранится имя переменной name и ее значение dval. Записи организованы в сингулярный набор env_var. Для этого набора определена функция поиска find_env_var.

В разделе main утилиты использованы два разных механизма описания переменых, типы которых требуют принудительного закрытия во избежание возможной утечки памяти. В нашем случае к таким типа относятся flex.t_lex и документ-очередь env.

Документ env xenv описан в разделе main после заголовка раздела перед блоком операторов. Эта конструкция называется суперблок и используется в нескольких местах синтаксиса генераторного языка, в частности как раз в разделе main утилиты. Что бы не произошло в блоке суперблока, организация сгенерированной программы обеспечит даже при фатальном завершении блока закрытие всех переменных суперблока.

Переменная flex.t_lex xlex описана в операторе call вызова процедуры. После ключевого слова call в скобках (вот для чего нужно это ключевое слово) описываются переменные, требюующие принудительного закрытия. Такая конструкция при трансяляции в Си подразумевает использование блока для описания этих переменных, вызов процедуры и, затем, вне зависимости от результата вызова (успешный или фатальный), закрытие всех переменных, после чего продолжение штатной работы при успехе или фатальный выход из данной процедуры.

Для тестирования в данном проекте задействованы два механизма описания директорий с данными.

В gen-файле описаны пакеты dirbin test и dirdbg test1. Это пакеты данных для копирования в бинарные директории (с результирующими исполняемыми файлами программ).

Пакету dirbin test соответствует файл описания пакета test.dirb. В файле располагется заголовок пакета с возможными опциями и перечень директорий и файлов. Сами файлы данных располагаются в директории fcalc/dirbin/test.

Пакету dirdbg test1 соответствует файл описания пакета test1.dird. В файле располагется заголовок пакета с возможными опциями и перечень директорий и файлов. Сами файлы данных располагаются в директории fcalc/dirdbg/test.

Отличие этих пакетов заключается в том, что пакет dirbin предусматривает возможность включения его файлов в инсталляционный пакет. Пакет dirdbg не предусматривает передачу его файлов в комплектах поставки, только для отладки.

Кроме того, директории и файлы пакета dirbin переписываются в бинарную директорию каждой предусмотренной в проекте платформы в виде поддиректории, а пакет dirdbg обеспечивает перепись его директорий и файлов непосредственно рядом с исполняемыми файлами.

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

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

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

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

test.dirb - файл каталога бинарных файлов проекта, с перечислением входящих в него файлов. /test/test.txt - файл тестовых данных.

test1.dird - файл каталога отладочных бинарных файлов проекта, с перечислением входящих в него файлов и директорий. /test1/test.txt - файл тестовых данных.

Файл fcalc.gen:

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

dirbin test

dirdbg test1

utility fcalc

Файл calc.utility:

utility fcalc:"File Calculator"

type t_name : char7[50];
type t_double : double;
  /frac=6
type env : dqueue
(
  record var
  ( t_name name,
    t_double dval
  );
  set env_var member var;
  find find_env_var set env_var(name);/oper=(first)
);

procspec syn_add(flex.t_lex xlex,env xenv,out t_double dval);

proc syn_fact(flex.t_lex xlex,env xenv,out t_double dval)
{ var
    t_name name;

  dval := 0.0;
  if ( flex.sample(xlex,"(") )
  { call syn_add(xlex,xenv,dval);
    flex.sample_err(xlex,")");
  }
  else if ( flex.double(xlex,NOSIGN,NOFRAC,NOEXPON,dval) )
    ;
  else if ( flex.ident(xlex,name) )
  { var
      var xvar;

    find_env_var_first(xenv,name,xvar);
    if ( isnull(xvar) )
      flex.message(xlex,"var not found");

    dval := xvar.dval;
  }
  else
    flex.message(xlex,"syntax error");
}

proc syn_mul(flex.t_lex xlex,env xenv,out t_double dval)
{ call syn_fact(xlex,xenv,dval);

  for ( ; ; )
  { var
      t_double dval1;

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

proc syn_add(flex.t_lex xlex,env xenv,out t_double dval)
{ var
    bool minus;

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

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

  for ( ; ; )
  { var
      t_double dval1;

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

proc syn(string8 n_file,env xenv)
{ varobj
    flex.t_lex xlex;

  flex.fopen(xlex,n_file);

  while ( flex.sample(xlex,"set") )
  { var
      var xvar,
      t_name name;

    flex.ident_err(xlex,name);
    find_env_var_first(xenv,name,xvar);
    if ( isnotnull(xvar) )
      flex.message(xlex,"duplicate var");

    var_cre(xenv,xvar);
    xvar.name := name;
    env_var_ins(xenv,xvar,-1);
    flex.sample_err(xlex,":=");
    call syn_add(xlex,xenv,xvar.dval);
    flex.sample_err(xlex,";");
  }

  flex.eof_err(xlex);
}

proc result(env xenv)
{ var
    var xvar;

  env_var_mem(xenv,0,xvar);

  while ( isnotnull(xvar) )
  { dprint(xvar.name," = ",xvar.dval,"\n");
    env_var_next(xvar);
  }
}

main
{ varobj
    env xenv;

  if ( utl.argc(xutl.yutl) <> 2 )
    error U"Не задан файл";

  var
    gsystem.t_filename8 n_file := utl.argv8(xutl.yutl,1);

  call syn(n_file,xenv);
  call result(xenv);
}
Скачать проект fcalc