Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
31
Добавлен:
02.05.2014
Размер:
21 Кб
Скачать
unit FormLab4;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls, ComCtrls, Grids, ExtCtrls,
  LexElem, SyntSymb, Triads;

type
  { Типы возможных ошибок компилятора: файловая,
    лексическая, синтаксическая, семантическая (триады)
    или ошибок нет }
  TErrType = (ERR_FILE, ERR_LEX, ERR_SYNT,
              ERR_TRIAD, ERR_NO);

  TCursovForm = class(TForm)
    PageControl1: TPageControl;
    SheetFile: TTabSheet;
    SheetLexems: TTabSheet;
    BtnExit: TButton;
    GroupText: TGroupBox;
    ListIdents: TMemo;
    EditFile: TEdit;
    BtnFile: TButton;
    BtnLoad: TButton;
    FileOpenDlg: TOpenDialog;
    GridLex: TStringGrid;
    SheetSynt: TTabSheet;
    TreeSynt: TTreeView;
    SheetTriad: TTabSheet;
    GroupTriadAll: TGroupBox;
    Splitter1: TSplitter;
    GroupTriadSame: TGroupBox;
    Splitter2: TSplitter;
    GroupTriadConst: TGroupBox;
    ListTriadAll: TMemo;
    ListTriadConst: TMemo;
    ListTriadSame: TMemo;
    CheckDel_C: TCheckBox;
    CheckDelSame: TCheckBox;
    SheetAsm: TTabSheet;
    ListAsm: TMemo;
    CheckAsm: TCheckBox;
    procedure BtnLoadClick(Sender: TObject);
    procedure BtnFileClick(Sender: TObject);
    procedure EditFileChange(Sender: TObject);
    procedure BtnExitClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject;
                        var Action: TCloseAction);
   private
    { Список лексем }
    listLex: TLexList;
    { Синтаксический стек }
    symbStack: TSymbStack;
    { Список триад }
    listTriad: TTriadList;
    { Имена файлов: входного, результата и ошибок }
    sInpFile,sOutFile,sErrFile: string;
    { Функция записи стартовых данных в файл ошибок }
    procedure StartInfo(const sErrF: string);
    { Функция обработки командной строки }
    procedure ProcessParams(
      var flOptC,flOptSame,flOptAsm: Boolean);
    { Процедура инициализации таблицы
      для отображения списка лексем }
    procedure InitLexGrid;
    { Процедура отображения синтаксического дерева }
    procedure MakeTree(nodeTree: TTreeNode;
                       symbSynt: TSymbol);
    { Процедура информации об ошибке }
    procedure ErrInfo(const sErrF,sErr: string;
                      iPos,iLen: integer);
    { Функция запуска компилятора }
    function CompRun(const sInF,sOutF,sErrF: string;
                    var symbRes: TSymbol;
                    flTrd,flDelC,flDelSame,flOptC,
                    flOptSame,flOptAsm: Boolean): TErrType;
   public
    { Public declarations }
  end;

var
  CursovForm: TCursovForm;

implementation

{$R *.DFM}

uses FncTree, LexType, LexAuto, TrdType, TrdMake, TrdAsm,
     TrdOpt;

procedure TCursovForm.InitLexGrid;
{ Процедура инициализации таблицы
  для отображения списка лексем }
begin
  with GridLex do
  begin
    RowCount := 2;
    Cells[0,0] := '№ п/п';
    Cells[1,0] := 'Лексема';
    Cells[2,0] := 'Значение';
    Cells[0,1] := '';
    Cells[1,1] := '';
    Cells[2,1] := '';
  end;
end;

procedure TCursovForm.StartInfo(
          const sErrF: string{имя файла ошибок});
{ Функция записи стартовых данных в файл ошибок }
var
  i,iCnt: integer;{счетчик параметров и переменная цикла}
  sTmp: string;{суммарная командная строка}
begin
  { Запоминаем имя файла ошибок }
  sErrFile := sErrF;
  { Записываем в файл ошибок дату запуска компилятора }
  ErrInfo(sErrFile,
          Format('--- %s ---',[DateTimeToStr(Now)]),0,0);
  { Берем количество входных параметров }
  iCnt := ParamCount;
  { Обнуляем суммарную командную строку }
  sTmp := ParamStr(0);
  { Записываем в суммарную командную строку
    все параметры последовательно }
  for i:=1 to iCnt do
   sTmp := sTmp +' '+ ParamStr(i);
  { Записываем в файл ошибок суммарную командную строку }
  ErrInfo(sErrFile,sTmp,0,0);
end;

procedure TCursovForm.ProcessParams(
var flOptC,flOptSame,flOptAsm: Boolean{флаги});
{ Функция обработки командной строки }
var
  i,iCnt,iLen: integer;
  sTmp: string;
  { Список для записи ошибок параметров }
  listErr: TStringList;
begin
  { Устанавливаем все флаги по умолчанию }
  flOptC    := True;
  flOptSame := True;
  flOptAsm  := True;
  { Создаем список для записи ошибок параметров }
  listErr := TStringList.Create;
  try
    { Берем количество входных параметров }
    iCnt := ParamCount;
    { Обрабатываем все параметры, начиная со второго }
    for i:=2 to iCnt do
    begin
      { Берем строку параметра }
      sTmp := ParamStr(i);
      { Берем длину строки параметра }
      iLen := Length(sTmp);
      { Если параметр слишком короткий или не начинается
        со знака "-" - это неправильный параметр }
      if (iLen < 3) or (sTmp[1] <> '-') then
       { Запоминаем ошибку в список }
       listErr.Add(Format('Неверный параметр %d: "%s"!',
                          [i,sTmp]))
      else
      { Иначе обрабатываем параметр в соответствии
        с его типом (второй символ) }
      case sTmp[2] of
        { Флаг оптимизации ассемблера }
        'a','A': flOptAsm  := (sTmp[3] = '1');
        { Флаг оптимизации методом свертки }
        'c','C': flOptC    := (sTmp[3] = '1');
        { Флаг оптимизации исключением лишних операций }
        's','S': flOptSame := (sTmp[3] = '1');
        { Имя выходного файла }
        'o','O': sOutFile := System.Copy(sTmp,3,iLen-2);
        { Имя файла ошибок }
        'e','E': StartInfo(System.Copy(sTmp,3,iLen-2));
        { Параметр неизвестного типа }
        else
         { Запоминаем ошибку в список }
         listErr.Add(Format('Неверный параметр %d: "%s"!',
                            [i,sTmp]));
      end{case};
    end{for};
    { Ставим имена файлов по умолчанию,
      если они не были указаны в параметрах }
    if sOutFile = '' then
      sOutFile := ChangeFileExt(sInpFile,'.asm');
    if sErrFile = '' then
      StartInfo(ChangeFileExt(sInpFile,'.err'));
    { Берем количество ошибок в параметрах }
    iCnt := listErr.Count-1;
    { Запоминаем информацию обо всех ошибках }
    for i:=0 to iCnt do ErrInfo(sErrFile,listErr[i],0,0)
    { Освобождаем память, занятую списком ошибок }
    finally listErr.Free;
  end{try};
end;

procedure TCursovForm.FormCreate(Sender: TObject);
var
  flOptC,flOptSame,flOptAsm: Boolean;
  symbRes: TSymbol;
  iErr: TErrType;
begin
  symbRes := nil;
  sOutFile := '';
  sErrFile := '';
  { В начале выполнения инициализируем список лесем,
    таблицу идентификаторов, синтаксический стек
    и список триад }
  InitTreeVar;
  listLex := TLexList.Create;
  symbStack := TSymbStack.Create;
  listTriad := TTriadList.Create;
  { Если указан параметр - не надо открывать окно,
    надо сразу запускать компилятор
    и обрабатывать входной файл }
  if ParamCount > 0 then
  begin
    { Берем имя входного файла из первого параметра }
    sInpFile := ParamStr(1);
    { Обрабатываем все остальные параметры }
    ProcessParams(flOptC,flOptSame,flOptAsm);
    { Запускаем компилятор }
    iErr := CompRun(
              sInpFile,sOutFile,sErrFile{входные файлы},
              symbRes{ссылка на дерево разбора},
              False{запоминать списки триад не надо},
              flOptC{флаг удаления триад "C"},
              flOptSame{флаг удаления триад "SAME"},
              flOptC{флаг оптимизации по методу
                     свертки объектного кода },
              flOptSame{флаг оптимизации исключ.
                        лишних операций},
              flOptAsm{флаг оптимизации команд
                       ассемблера});
    { Если не было файловых ошибок,
      то надо завершать работу }
    if iErr <> ERR_FILE then Self.Close;
  end;
end;

procedure TCursovForm.FormClose(Sender: TObject;
                            var Action: TCloseAction);
{ В конце выполнения очищаем список лесем,
  таблицу идентификаторов, синтаксический стек
  и список триад }
begin
  listTriad.Free;
  symbStack.Free;
  listLex.Free;
  ClearTreeVar;
  Application.Terminate;
end;

procedure TCursovForm.EditFileChange(Sender: TObject);
begin
  { Можно читать файл, только когда его имя не пустое }
  BtnLoad.Enabled := (EditFile.Text <> '');
end;

procedure TCursovForm.BtnFileClick(Sender: TObject);
begin
  if FileOpenDlg.Execute then
  { Выбор имени файла с помощью стандартного диалога }
  begin
    EditFile.Text := FileOpenDlg.FileName;
    BtnLoad.Enabled := (EditFile.Text <> '');
  end;
end;

procedure TCursovForm.ErrInfo(const sErrF,sErr: string;
                              iPos,iLen: integer);
{ Процедура информации об ошибке }
var
{ Файл для записи информации об ошибке }
  fileErr: TextFile;
begin
  { Если имя файла ошибок не пустое }
  if sErrF <> '' then
  try
    { Записываем информацию об ошибке в файл }
    AssignFile(fileErr,sErrF);
    if FileExists(sErrF) then Append(fileErr)
                         else Rewrite(fileErr);
    writeln(fileErr,sErr);
    { и закрываем его }
    CloseFile(fileErr);
    except
      { Если была ошибка записи в файл, то
        выдаем сообщение об этом }
      MessageDlg(Format('Ошибка записи в файл "%s"!'#13#10
                      + 'Ошибка компиляции: %s!',
                        [sErrF,sErr]),
                 mtError,[mbOk],0);
  end
  else
  { Если имя файла ошибок пустое,
    выводим информацию на экран }
  begin
    { Позиционируем список строк на место ошибки }
    ListIdents.SelStart  := iPos;
    ListIdents.SelLength := iLen;
    { Выводим сообщение на экран }
    MessageDlg(sErr,mtWarning,[mbOk],0);
    { Отображаем позицию ошибки в списке строк }
    ListIdents.SetFocus;
  end;
end;

{ Функция запуска компилятора }
function TCursovForm.CompRun(
  const sInF,{имя входного файла}
       sOutF,{имя результирующего файла}
       sErrF{имя файла ошибок}:string;
       var symbRes: TSymbol;{корень дерева разбора}
       flTrd,{флаг записи триад в списки}
       flDelC,{флаг удаления триад типа ""}
       flDelSame,{флаг удаления триад типа ""}
       flOptC,{флаг оптимизации методом свертки}
       flOptSame,{флаг оптимизации методом
                  исключения лишних операций}
       flOptAsm{флаг оптимизации ассемблерного кода}
       : Boolean): TErrType;
var
  i,iCnt,iErr: integer;
  lexTmp: TLexem;
  sVars,sAdd: string;
  asmList: TStringList;
begin
  { Очищаем список лексем }
  listLex.Clear;
  { Очищаем синтаксический стек }
  symbStack.Clear;
  { Очищаем список триад }
  listTriad.Clear;
  try
    { Чтение файла в список строк }
    ListIdents.Lines.LoadFromFile(sInF);
    except
      Result := ERR_FILE;
      MessageDlg('Ошибка чтения файла!',mtError,[mbOk],0);
      Exit;
  end;
  { Анализ списка строк и заполнение списка лексем }
  iErr := MakeLexList(ListIdents.Lines,listLex);
  if iErr <> 0 then
  { Если анализ не успешный, выводим сообщение об ошибке }
  begin
    { Берем позицию ошибочной лексемы из
      фиктивной лексемы в начале списка }
    ErrInfo(sErrF,
            Format('Неверная лексема "%s" в строке %d!',
                   [listLex[0].LexInfoStr,iErr]),
            listLex[0].PosAll,listLex[0].PosNum);
    { Результат - лексическая ошибка }
    Result := ERR_LEX;
  end
  else
  begin
    { Добавляем в конец списка лексем информационную
      лексему "конец строки" }
    with ListIdents do
     listLex.Add(TLexem.CreateInfo('Конец строки',
                          Length(Text),Lines.Count-1,0));
    { Строим дерево синтаксического разбора и
      получаем ссылку на его корень }
    symbRes := BuildSyntList(listLex,symbStack);
    { Если эта ссылка содержит лексические данные,
      значит была ошибка в месте, указанном лексемой }
    if symbRes.SymbType = SYMB_LEX then
    begin
      { Берем позицию ошибочной лексемы из
        лексемы по ссылке }
      ErrInfo(sErrF,
       Format('Синтаксическая ошибка в строке %d поз. %d!',
            [symbRes.Lexem.StrNum+1,symbRes.Lexem.PosNum]),
       symbRes.Lexem.PosAll,0);
      { Освобождаем ссылку на лексему }
      symbRes.Free;
      symbRes := nil;
      { Результат - синтаксическая ошибка }
      Result := ERR_SYNT;
    end
    else
    { Иначе ссылка указывает на корень
      синтаксического дерева }
    begin
      { Строим список триад по синтаксическому дереву }
      lexTmp := MakeTriadList(symbRes,listTriad);
      { Если есть ссылка на лексему, значит была
        семантическая ошибка }
      if lexTmp <> nil then
      begin
        { Берем позицию ошибочной лексемы по ссылке }
        ErrInfo(sErrF,
        Format('Семантическая ошибка в строке %d поз. %d!',
               [lexTmp.StrNum+1,lexTmp.PosNum]),
        lexTmp.PosAll,0);
        { Результат - семантическая ошибка }
        Result := ERR_TRIAD;
      end
      else
      { Если есть ссылка пуста, значит триады построены }
      begin
        { Результат - "ошибок нет" }
        Result := ERR_NO;
        { Если указан флаг, сохраняем общий список триад }
        if flTrd then
          listTriad.WriteToList(ListTriadAll.Lines);
        { Если указан флаг, выполняем оптимизацию
          методом свертки объектного кода }
        if flOptC then
        begin
          OptimizeConst(listTriad);
          { Если указан флаг, удаляем триады типа "C" }
          if flDelC then
            DelTriadTypes(listTriad,TRD_CONST);
        end;
        { Если указан флаг, сохраняем
          список триад после оптимизации }
        if flTrd then
          listTriad.WriteToList(ListTriadConst.Lines);
        { Если указан флаг, выполняем оптимизацию
          методом исключения лишних операций }
        if flOptSame then
        begin
          OptimizeSame(listTriad);
          { Если указан флаг, удаляем триады типа "SAME" }
          if flDelSame then
            DelTriadTypes(listTriad,TRD_SAME);
        end;
        { Если указан флаг, сохраняем
          список триад после оптимизации }
        if flTrd then
          listTriad.WriteToList(ListTriadSame.Lines);
        { Распределяем регистры по списку триад }
        iCnt := MakeRegisters(listTriad);
        { Создаем и записываем список ассемблерных команд }
        asmList := TStringList.Create;
        try
          with asmList do
          begin
            { Очищаем список ассемблерных команд }
            Clear;
            { Пишем заголовок программы }
            Add(Format('program %s;',[NAME_PROG]));
            { Запоминаем перечень всех идентификаторов }
            sVars := IdentList(',',NAME_INPVAR,NAME_FUNCT);
            if sVars <> '' then
            begin
              { Если перечень идентификаторов не пустой,
                записываем его с указанием типа данных }
              Add('');
              Add('var');
              Add(Format('  %s: %s;',[sVars,NAME_TYPE]));
            end;
            Add('');
            { Пишем заголовок функции }
            Add(Format('function %0:s(%1:s: %2:s): %2:s;'
                      +' stdcall;',
                      [NAME_FUNCT,NAME_INPVAR,NAME_TYPE]));
            { Если регистров для хранения промежуточных
              результатов не хватило и нужны временные
              переменные, то заполняем их список }
            if iCnt > 0 then
            begin
              Add('var');
              sVars := '';
              for i:=0 to iCnt do
              begin
                sAdd := Format('%s%d',[TEMP_VARNAME,i]);
                if sVars = '' then sVars := sAdd
                else sVars := sVars +','+ sAdd;
              end;
              Add(Format('  %s: %s;',[sVars,NAME_TYPE]));
            end;
            { В тело функции записываем созданный список
              команд ассемблера }
            Add('begin');
            Add('  asm');
            Add(#9'pushad'#9#9'{запоминаем регистры}');
            MakeAsmCode(listTriad,asmList,flOptAsm);
            Add(#9'popad'#9#9'{восстанавливаем регистры}');
            Add('  end;');
            Add('end;');
            Add('');
            { Описываем одну входную переменную }
            Add(Format('var %s: %s;',
                       [NAME_INPVAR,NAME_TYPE]));
            Add('');
            { Заполняем тело главной программы }
            Add('begin');
            Add(Format('  readln(%s);',[NAME_INPVAR]));
            Add(Format('  writeln(%s(%s));',
                       [NAME_FUNCT,NAME_INPVAR]));
            Add('  readln;');
            Add('end.');
          end{with};
          { Если установлен флаг, записываем список команд
            в список для отображения на экране }
          if flTrd then
           ListAsm.Lines.AddStrings(asmList);
          { Если имя результирующего файла не пустое,
            записываем туда список всех команд }
          if sOutF <> '' then
          try
            asmList.SaveToFile(sOutF);
            except Result := ERR_FILE;
          end;
          finally asmList.Free;
        end{try};
      end;
    end;
  end;
end;

procedure TCursovForm.BtnLoadClick(Sender: TObject);
{ Процедура чтения и анализа файла }
var
  i,iCnt: integer;
  iRes: TErrType;
  symbRes: TSymbol;
  nodeTree: TTreeNode;
begin
  symbRes := nil;
  { Очищаем таблицу отображения списка лексем
    и синтаксическое дерево }
  InitLexGrid;
  TreeSynt.Items.Clear;
  { Вызываем функцию компиляции }
  iRes := CompRun(
            EditFile.Text,'','',{задан только входной файл}
            symbRes{указатель на дерево разбора},
            True{Списки триад нужно запоминать},
            CheckDel_C.Checked
            {флаг удаления триад "C"},
            CheckDelSame.Checked
            {флаг удаления триад "SAME"},
            True{флаг оптимизации по методу
                 свертки объектного кода },
            True{флаг оптимизации исключ.
                 лишних операций},
            CheckAsm.Checked{флаг оптимизации
                             команд ассемблера});
  if iRes > ERR_LEX then
  { Если не было лексической ошибки,
    заполняем список лепксем }
  begin
    { Цикл по всем прочитанным лексемам }
    GridLex.RowCount := listLex.Count+1;
    iCnt := listLex.Count-1;
    for i:=0 to iCnt do
    begin
      { Первая колонка - номер }
      GridLex.Cells[0,i+1] := IntToStr(i+1);
      { Вторая колонка - тип лексемы }
      GridLex.Cells[1,i+1] :=
        LexTypeName(listLex[i].LexType);
      { Третья колонка - значение лексемы }
      GridLex.Cells[2,i+1] := listLex[i].LexInfoStr;
    end;
  end;
  if (iRes > ERR_SYNT) and (symbRes <> nil) then
  { Если не было синтаксической ошибки,
    заполняем дерево синтаксического разбора }
  begin
    { Записываем данные в корень дерева }
    nodeTree := TreeSynt.Items.Add(nil,symbRes.SymbolStr);
    { Строим остальные элементы дерева от его корня }
    MakeTree(nodeTree,symbRes);
    { Раскрываем всё дерево }
    nodeTree.Expand(True);
    { Позиционируем указатель на корневой элемент }
    TreeSynt.Selected := nodeTree;
  end;
  if iRes > ERR_TRIAD then
  { Если не было семантической ошибки,
    то компиляция успешно запвершена }
  begin
    MessageDlg('Компиляция успешно выполнена!',
               mtInformation,[mbOk],0);
    PageControl1.ActivePageIndex := 4;
  end;
end;

procedure TCursovForm.MakeTree(
{ Процедура отображения синтаксического дерева }
  nodeTree: TTreeNode;
  {ссылка на корневой элемент отображаемой
   части дерева на экране}
  symbSynt: TSymbol
  {ссылка на синтаксический символ, связанный
   с корневым элементом этой части дерева});
var
  i,iCnt: integer;
  nodeTmp: TTreeNode;
begin
  { Берем количество дочерних вершин для текущей }
  iCnt := symbSynt.Count-1;
  { Цикл по всем дочерним вершинам }
  for i:=0 to iCnt do
  begin
    { Добавляем к дереву на экране вершину и
      запоминаем ссылку на нее }
    nodeTmp := TreeSynt.Items.AddChild(nodeTree,
                                symbSynt[i].SymbolStr);
    { Если эта вершина связана с нетерминальным символом,
      рекурсивно вызываем процедуру построения дерева
      для нее }
    if symbSynt[i].SymbType = SYMB_SYNT then
     MakeTree(nodeTmp,symbSynt[i]);
  end;
end;

procedure TCursovForm.BtnExitClick(Sender: TObject);
{ Завершение работы с программой }
begin
  Self.Close;
end;

end.
Соседние файлы в папке Cursov