Создание синтаксического анализатора простого языка программирования — различия между версиями

Материал из Вики ИТ мехмата ЮФУ
Перейти к: навигация, поиск
(Комментарии к синтаксическому анализатору)
(Комментарии к синтаксическому анализатору)
Строка 1: Строка 1:
 
[[Страница_курса_%22Методы_построения_компиляторов%22| К основной странице курса]]
 
[[Страница_курса_%22Методы_построения_компиляторов%22| К основной странице курса]]
  
==== Комментарии к синтаксическому анализатору====  
+
=== Комментарии к синтаксическому анализатору===
 
Мы пишем синтаксический анализатор методом рекурсивного спуска. Для этого грамматика должна быть праворекурсивной.
 
Мы пишем синтаксический анализатор методом рекурсивного спуска. Для этого грамматика должна быть праворекурсивной.
 
Начальная грамматика нашего языка имеет вид:
 
Начальная грамматика нашего языка имеет вид:

Версия 11:12, 15 августа 2014

К основной странице курса

Комментарии к синтаксическому анализатору

Мы пишем синтаксический анализатор методом рекурсивного спуска. Для этого грамматика должна быть праворекурсивной. Начальная грамматика нашего языка имеет вид:

Грамматика:
 progr -> block 
 stlist -> st ; stlist | st
 st -> assign | block | cycl
 assign -> id signa expr
 signa -> := | += | -= | *=
 expr -> id | num
 block -> begin stlist end
 cycl -> cycle expr st   

Здесь правило

 stlist -> st ; stlist | st

праворекурсивно. Следует обратить внимание, что точка с запятой разделяет операторы, а не завершает их.

Примеры правильных программ в данной грамматике:

begin
  a := 2
end
begin
  a := 2;
  cycle a
  begin
    b := a;
    c := 234
  end
end
begin
  b := 2
end

Примеры неправильных программ в данной грамматике:

a := 2;
begin
  b := 2
end.

При написании синтаксического анализатора методом рекурсивного спуска следует для каждого нетерминала реализовать процедуру, которая будет распознавать этот нетерминал. При реализации необходимо придерживаться следующей стратегии: перед вызовом каждой процедуры для нетерминала считается, что первая лексема уже считана с помощью NextLexem. Кроме того, каждая процедура после распознавания своей конструкции считывает следующую лексему с помощью NextLexem, подготавливая вызов следующей процедуры.

Ошибки, распознаваемые синтаксическим анализатором, называются синтаксическими и выводятся вызовом процедуры syntaxerror.

Отметим также, что приведенный парсер (синтаксический анализатор) не выполняет никаких семантических действий - он только распознает, принадлежит ли программа языку, порождаемому данной грамматикой.

Модуль синтаксического анализатора (парсера)

// Распознавание программы на простом языке. Распознаватель грамматики (парсер)
unit SimpleLangParser;

interface

procedure Progr;
procedure Expr;
procedure Assign;
procedure StatementList;
procedure Statement; 
procedure Block;
procedure Cycle;
procedure syntaxerror(message: string := '');

implementation

uses SimpleLangLexer;

procedure Progr;
begin
  Block
end;

procedure Statement;
begin
  case LexKind of
lexBegin: Block; 
lexCycle: Cycle;
lexId: Assign;
else syntaxerror('Ожидался оператор');
  end;
end;

procedure Block;
begin
  NextLexem; // Пропуск begin
  StatementList;
  if LexKind=lexEnd then
    NextLexem
  else syntaxerror('Ожидалось end');    
end;

procedure Cycle;
begin
  NextLexem; // Пропуск cycle
  Expr;
  Statement;
end;

procedure Assign;
begin
  NextLexem; // Пропуск id
  if LexKind in [lexAssign..lexAssignMult] then
    NextLexem
  else syntaxerror('Ожидалось :=');  
  Expr;  
end;

procedure StatementList;
begin
  Statement;
  while LexKind=lexSemicolon do
  begin
    NextLexem;
    Statement;
  end
end;

procedure Expr;
begin
  if LexKind in [lexId,lexNum] then
    NextLexem 
  else syntaxerror('Ожидалось выражение');  
end;

procedure syntaxerror(message: string);
begin
  var ss := System.IO.File.ReadLines(fname).Skip(LexRow-1).First(); // Строка row файла
  writeln('Синтаксическая ошибка в строке ',LexRow,':');
  writeln(ss);
  writeln('^':LexCol-1);
  if message<>'' then 
    writeln(message);
  Done;  
  halt;
end;
 
end.

Основная программа

uses SimpleLangLexer,SimpleLangParser;

begin 
  Init('progr.txt');
  Progr;
  if LexKind=lexEot then
    writeln('программа распознана правильно ')
  else syntaxerror('ожидался конец файла'); 
end.

Задания

  1. Добавить цикл for
  2. Добавить простые логические выражения вида x<0 (в лексический анализатор придется добавлять знаки отношений, а в синтаксический - правила грамматики
logicalexpr ::= id_or_num relop id_or_num
id_or_num ::= id | num
  1. Добавить оператор if (предусмотреть полную и неполную форму)
  2. Добавить оператор while
  3. Добавить грамматику выражений
E ::= T A
A ::= ε | + T A | - T A
T ::= M B
B ::= ε | * M B | / M B
M ::= id | num | (E)

Грамматика составлена так, что по ней можно составить ручной анализатор методом рекурсивного спуска.