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

interface
{ Модуль, обеспечивающий выполнение функций синтаксического
  разбора с помощью алгоритма "сдвиг-свертка" на основе
  матрицы операторного предшествования }

uses Classes, LexElem, SyntRule;

type
{ Два типа символов: терминальные (лексемы)
  и нетерминальные (синтаксические) }
TSymbKind = (SYMB_LEX, SYMB_SYNT);

{ Структура данных для информации о символе грамматики }
TSymbInfo = record
  { Тип символа - терминальный или нетерминальный }
  case SymbType: TSymbKind of
    { Для терминального символа - ссылка на лексему }
    SYMB_LEX:  (LexOne: TLexem);
    { Для нетерминального символа - ссылка на список
      символов, из которых он был построен }
    SYMB_SYNT: (LexList: TList);
end;

{ Предварительное описание класса "Символ" }
TSymbol = class;
{ Массив символов, составляющих правило грамматики }
TSymbArray = array[0..RULE_LENGTH] of TSymbol;

{ Структура, описывающая грамматический символ }
TSymbol = class(TObject)
 protected
  { Информация о символе }
  SymbInfo: TSymbInfo;
  { Номер правила, по которому создан символ }
  iRuleNum: integer;
 public
  { Конструктор создания терминального символа
    на основе лексемы }
  constructor CreateLex(Lex: TLexem);
  { Конструктор создания нетерминального символа
    на основе правила и массива символов }
  constructor CreateSymb(iR,iSymbN: integer;
                         const SymbArr: TSymbArray);
  { Деструктор для удаления символа }
  destructor Destroy; override;
  { Функция получения символа из правила по порядковому
    номеру символа }
  function GetItem(iIdx: integer): TSymbol;
  { Функция, возвращающее количество символов
    в исходном правиле }
  function Count: integer;
  { Функция, формирующая строковое представление символа }
  function SymbolStr: string;
  { Свойства символа на основе описанных выше
    функций и данных }
  { Свойство, возвращающее тип символа }
  property SymbType: TSymbKind read SymbInfo.SymbType;
  { Свойство, возвращающее ссылку на лексему
    для терминального символа }
  property Lexem: TLexem read SymbInfo.LexOne;
  { Свойство, возвращающее символа из правила
    по порядковому номеру символа }
  property Items[iIdx: integer]: TSymbol read GetItem;
   default;
  { Свойство, возвращающее номер правила }
  property Rule: integer read iRuleNum;
end;

{ Структура, описывающая синтаксический стек }
TSymbStack = class(TList)
 public
  { Деструктор для удаления стека }
  destructor Destroy; override;
  { Функция очистки стека }
  procedure Clear; override;
  { Функция выборки символа по порядковому номеру
    от конца стека }
  function GetSymbol(iIdx: integer): TSymbol;
  { Функция помещения в стек входящей лексемы }
  function Push(lex: TLexem): TSymbol;
  { Свойство для выборки символа по порядковому номеру
    от вершины стека }
  property Symbols[iIdx: integer]: TSymbol read GetSymbol;
   default;
  { Функция, возвращающая самую верхнюю лексему в стеке }
  function TopLexem: TLexem;
  { Функция, выполняющая свертку и помещающая новый символ
    на вершину стека }
  function MakeTopSymb: TSymbol;
end;

{ Функция, выполняющая алгоритм "сдвиг-свертка" }
function BuildSyntList(const listLex: TLexList;
                       symbStack: TSymbStack): TSymbol;

implementation

uses LexType, LexAuto;

constructor TSymbol.CreateLex(Lex: TLexem);
{ Конструктор создания терминального символа
  на основе лексемы }
begin
  { Вызываем конструктор базового класа }
  inherited Create;
  { Ставим тип символа "терминальный" }
  SymbInfo.SymbType := SYMB_LEX;
  { Запоминаем ссылку на лексему }
  SymbInfo.LexOne := Lex;
  { Правило не используется, поэтому "0" }
  iRuleNum := 0;
end;

constructor TSymbol.CreateSymb(iR{номер правила},
  iSymbN{кол-во исходных символов}: integer;
  const SymbArr: TSymbArray{массив исходных символов});
{ Конструктор создания нетерминального символа
  на основе правила и массива символов }
var i: integer;
begin
  { Вызываем конструктор базового класа }
  inherited Create;
  { Ставим тип символа "нетерминальный" }
  SymbInfo.SymbType := SYMB_SYNT;
  { Создаем список для хранения исходных символов }
  SymbInfo.LexList := TList.Create;
  { Переносим все исходные символы в список
    в обратном порядке }
  for i:=iSymbN-1 downto 0 do
   SymbInfo.LexList.Add(SymbArr[i]);
  { Запоминаем номер правила }
  iRuleNum := iR;
end;

function TSymbol.GetItem(iIdx: integer): TSymbol;
{ Функция получения символа из правила
  по порядковому номеру символа }
begin
  Result := TSymbol(SymbInfo.LexList[iIdx])
end;

function TSymbol.Count: integer;
{ Функция, возвращающее количество символов
  в исходном правиле }
begin
  Result := SymbInfo.LexList.Count;
end;

function TSymbol.SymbolStr: string;
{ Функция, формирующая строковое представление символа }
begin
  { Если это нетерминальный символ, формируем его
    представление в зависимости от номера правила }
  if SymbType = SYMB_SYNT then
   Result := MakeSymbolStr(iRuleNum)
  { Если это терминальный символ, формируем его
    представление в соответствии со строковым
    представлением лексемы }
  else Result := Lexem.LexInfoStr;
end;

destructor TSymbol.Destroy;
{ Деструктор для удаления символа }
var i: integer;
begin
  if SymbInfo.SymbType = SYMB_SYNT then
  { Если это нетерминальный символ }
  with SymbInfo.LexList do
  begin
    { Удаляем все его исходные символы из списка }
    for i:=Count-1 downto 0 do TSymbol(Items[i]).Free;
    { Удаляем список символов }
    Free;
  end;
  { Вызываем деструктор базового класа }
  inherited Destroy;
end;

destructor TSymbStack.Destroy;
{ Деструктор для удаления синтаксического стека }
begin
  { Очищаем стек }
  Clear;
  { Вызываем деструктор базового класа }
  inherited Destroy;
end;

procedure TSymbStack.Clear;
{ Функция очистки синтаксического стека }
var i: integer;
begin
  { Удаляем все символы из стека }
  for i:=Count-1 downto 0 do TSymbol(Items[i]).Free;
  { Вызываем функцию базового класса }
  inherited Clear;
end;

function TSymbStack.GetSymbol(iIdx: integer): TSymbol;
{ Функция выборки символа по порядковому номеру
  от конца стека }
begin
  Result := TSymbol(Items[iIdx]);
end;

function TSymbStack.TopLexem: TLexem;
{ Функция, возвращающая самую верхнюю лексему
  в синтаксическом стеке }
var i: integer;
begin
  Result := nil;
  { Для всех символов начиная от вершины стека }
  for i:=Count-1 downto 0 do
   { Если это терминальный символ }
   if Symbols[i].SymbType = SYMB_LEX then
   begin
     { Берем ссылку на лексему }
     Result := Symbols[i].Lexem;
     { Прекращаем поиск }
     Break;
   end;
end;

function TSymbStack.Push(lex: TLexem): TSymbol;
{ Функция помещения лексемы в синтаксический стек }
begin
  { Создаем новый терминальный символ }
  Result := TSymbol.CreateLex(lex);
  { Добавляем его в стек }
  Add(Result);
end;

function TSymbStack.MakeTopSymb: TSymbol;
{ Функция, выполняющая свертку и помещающая новый символ
  на вершину стека.
  Результат функции:
  nil - если не удалось выполнить свертку, иначе ссылка
  на новый нетерминальный символ - если свертка выполнена }
var
  symCur: TSymbol; {текущий символ стека}
  SymbArr: TSymbArray;
  {массив для запоминания символов правила}
  i,iSymbN: integer;
  {счетчик символов стеке и счетчик символов в правиле}
  sRuleStr: string; {строковое представление правила}
{ Функция добавления символа в правило }
procedure AddToRule(const sStr: string;{строка символа}
                    sym: TSymbol{тек. символ});
begin
  { Устанавливаем ссылку на текущий символ }
  symCur := sym;
  { Добавляем очередной символ в массив символов правила }
  SymbArr[iSymbN] := Symbols[i];
  { Добавляем его в строковое представление правила
    (слева!) }
  sRuleStr := sStr + sRuleStr;
  { Удаляем символ из стека }
  Delete(i);
  { Увеличиваем счетчик символов в правиле }
  Inc(iSymbN);
end;
begin
  Result := nil;
  { Сбрасываем счетчик символов в правиле и
    обнуляем текущий символ }
  iSymbN := 0;
  symCur := nil;
  { Строковое представление правила пустое }
  sRuleStr := '';
  { Выполняем алгоритм для всех символов,
    начиная с вершины стека }
  for i:=Count-1 downto 0 do
  begin
    { Если это нетерминальный (синтаксический) символ }
    if Symbols[i].SymbType = SYMB_SYNT then
      { Добавляем его в правило, текущий символ при этом
        не меняется }
      AddToRule(Symbols[i].SymbolStr,symCur)
    else
    { Если это терминальный символ и
      текущий символ пустой }
    if symCur = nil then
      { Добавляем его в правило и делаем текущим }
      AddToRule(LexTypeInfo(Symbols[i].Lexem.LexType),
                Symbols[i])
    else
    { Если это терминальный символ и он связан
      отношением "=" с текущим символов }
    if GramMatrix[Symbols[i].Lexem.LexType,
                  symCur.Lexem.LexType] = '=' then
      { Добавляем его в правило и делаем текущим }
      AddToRule(LexTypeInfo(Symbols[i].Lexem.LexType),
                Symbols[i])
    else
     { Иначе прерываем цикл, дальше искать не нужно }
      Break;
    { Если превышена максимальная длина правила,
      цикл прекращаем }
    if iSymbN > RULE_LENGTH then Break;
  end;
  { Если выбран хотя бы один символ из стека }
  if iSymbN <> 0 then
  begin
    { Ищем простым перебором правило, у которого строковое
      представление совпадает с построенным
      строковым представлением }
    for i:=1 to RULE_NUM do
     if GramRules[i] = sRuleStr then
     begin
       { Если правило найдено, создаем новый
         нетерминальный символ }
       Result := TSymbol.CreateSymb(i,iSymbN,SymbArr);
       { И добавляем его в стека }
       Add(Result);
       { Прерываем цикл поика правил }
       Break;
     end;
    { Если не был создан новый символ (правило не найдено),
      надо удалить все исходные символы, это ошибка }
    if Result = nil then
     for i:=0 to iSymbN-1 do SymbArr[i].Free;
  end;
end;

function BuildSyntList(
  const listLex: TLexList{входная таблица лексем};
  symbStack: TSymbStack{стек для работы алгоритма}
  ): TSymbol;
{ Функция, выполняющая алгоритм "сдвиг-свертка".
  Результат функции:
  - нетерминальный символ (корень синтаксического дерева),
    если разбор был выполнен успешно;
  - терминальный символ, ссылающийся на лексему, где была
    обнаружена ошибка, если разбор выполнен с ошибками. }
var
  i,iCnt: integer; {счетчик лексем и длина таблицы лексем}
  lexStop: TLexem;
  {ссылка на дополнительную начальную лексему}
  lexTCur: TLexType;{тип текущей лексемы}
  cRule: char;{текущее отношение предшествования}
begin
  Result := nil;
  { Вычисляем длину входной таблицы лексем }
  iCnt := listLex.Count-1;
  { Создаем дополнительную лексему "начало строки" }
  lexStop := TLexem.CreateInfo('Начало файла',0,0,0);
  try
    { Помещаем начальную лексему в стек }
    symbStack.Push(lexStop);
    { Обнуляем счетчик входных лексем }
    i := 0;
    { Цикл по всем лексемам от начала
      до конца таблицы лексем }
    while i<=iCnt do
    begin
      { Получаем тип лексемы на вершине стека }
      lexTCur := symbStack.TopLexem.LexType;
      { Если на вершине стека начальная лексема,
        а текущая лексема - конечная,
        то алгоритм разбора завершен }
      if (lexTCur = LEX_START)
       and (listLex[i].LexType = LEX_START) then Break;
      { Смотрим отношение лексемы на вершине стека
        и текущей лексемы в строке }
      cRule := GramMatrix[lexTCur,listLex[i].LexType];
      { Корректируем отношение. Если корректировка матрицы
        предшествования не используется, то функция должна
        вернуть то же самое отношение  }
      cRule := CorrectRule(cRule,lexTCur,
                           listLex[i].LexType,symbStack);
      case cRule of
        '<','=': { Надо выполнять сдвиг (перенос) }
          begin
            { Помещаем текущую лексему в стек }
            symbStack.Push(listLex[i]);
            { Увеличиваем счетчик входных лексем }
            Inc(i);
          end;
        '>': { Надо выполнять свертку }
          if symbStack.MakeTopSymb = nil then
          { Если не удалось выполнить свертку }
          begin
            { Запоминаем текущую лексему как место ошибки }
            Result := TSymbol.CreateLex(listLex[i]);
            { Прерываем алгоритм }
            Break;
          end;
        else
         { Отношение не установлено - ошибочная ситуация }
          begin
            { Запоминаем текущую лексему как место ошибки }
            Result := TSymbol.CreateLex(listLex[i]);
            { Прерываем алгоритм }
            Break;
          end;
      end{case};
    end{while};
    { Если алгоритм прошел без ошибок }
    if Result = nil then
    begin
      { Проверяем, что в стеке осталось только 2 символа }
      if symbStack.Count = 2 then
        { Если да, то верхний символ -
          результат синтаксического разбора }
        Result := symbStack[1]
      { Иначе это ошибка - отмечаем последнюю лексему
        как место ошибки }
      else Result := TSymbol.CreateLex(listLex[iCnt]);
    end;
    finally
      { Уничтожаем временную начальную лексему }
      lexStop.Free;
  end;
end;

end.

Соседние файлы в папке Common