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

Материал из Вики ИТ мехмата ЮФУ
Перейти к: навигация, поиск
(Комментарии к синтаксическому анализатору)
(Дополнительные задания (5 баллов))
 
(не показано 20 промежуточных версий этого же участника)
Строка 1: Строка 1:
 
[[Страница_курса_%22Методы_построения_компиляторов%22| К основной странице курса]]
 
[[Страница_курса_%22Методы_построения_компиляторов%22| К основной странице курса]]
 
+
__NOTOC__
 
=== Комментарии к синтаксическому анализатору===
 
=== Комментарии к синтаксическому анализатору===
 
Мы пишем синтаксический анализатор '''методом рекурсивного спуска'''. Для этого грамматика должна быть ''праворекурсивной''.
 
Мы пишем синтаксический анализатор '''методом рекурсивного спуска'''. Для этого грамматика должна быть ''праворекурсивной''.
Начальная грамматика нашего языка имеет вид:
+
 
Грамматика:
+
Грамматика нашего языка имеет вид:
 
   progr -> block  
 
   progr -> block  
 
   stlist -> statement ; stlist | statement
 
   stlist -> statement ; stlist | statement
Строка 18: Строка 18:
  
 
Примеры правильных программ в данной грамматике:
 
Примеры правильных программ в данной грамматике:
<pre>
+
 
begin
+
<source lang="Delphi">begin
 
   a := 2
 
   a := 2
end
+
end</source>
</pre>
+
 
 +
 
  
<pre>
+
<source lang="Pascal">begin
begin
 
 
   a := 2;
 
   a := 2;
 
   cycle a
 
   cycle a
Строка 32: Строка 32:
 
     c := 234
 
     c := 234
 
   end
 
   end
end
+
end</source>
</pre>
+
 
  
<pre>
+
<source lang="Pascal">
 
begin
 
begin
 
   b := 2
 
   b := 2
end
+
end</source>
</pre>
+
 
  
 
Примеры неправильных программ в данной грамматике:
 
Примеры неправильных программ в данной грамматике:
Строка 60: Строка 60:
 
=== Модуль синтаксического анализатора (парсера) ===
 
=== Модуль синтаксического анализатора (парсера) ===
 
<source lang="Delphi">
 
<source lang="Delphi">
// Распознавание программы на простом языке. Распознаватель грамматики (парсер)
 
 
unit SimpleLangParser;
 
unit SimpleLangParser;
 
+
 
interface
 
interface
 
+
 
procedure Progr;
 
procedure Progr;
 
procedure Expr;
 
procedure Expr;
Строка 73: Строка 72:
 
procedure Cycle;
 
procedure Cycle;
 
procedure syntaxerror(message: string := '');
 
procedure syntaxerror(message: string := '');
 
+
 
implementation
 
implementation
 
+
uses SimpleLangLexer;
+
uses  
 
+
  SimpleLangLexer;
 +
 
procedure Progr;
 
procedure Progr;
 
begin
 
begin
 
   Block
 
   Block
 
end;
 
end;
 
+
 
procedure Statement;
 
procedure Statement;
 
begin
 
begin
 
   case LexKind of
 
   case LexKind of
lexBegin: Block;  
+
Tok.&Begin: Block;  
lexCycle: Cycle;
+
Tok.Cycle: Cycle;
lexId: Assign;
+
Tok.Id: Assign;
 
else syntaxerror('Ожидался оператор');
 
else syntaxerror('Ожидался оператор');
 
   end;
 
   end;
 
end;
 
end;
 
+
 
procedure Block;
 
procedure Block;
 
begin
 
begin
 
   NextLexem; // Пропуск begin
 
   NextLexem; // Пропуск begin
 
   StatementList;
 
   StatementList;
   if LexKind=lexEnd then
+
   if LexKind=Tok.&End then
 
     NextLexem
 
     NextLexem
 
   else syntaxerror('Ожидалось end');     
 
   else syntaxerror('Ожидалось end');     
 
end;
 
end;
 
+
 
procedure Cycle;
 
procedure Cycle;
 
begin
 
begin
Строка 108: Строка 108:
 
   Statement;
 
   Statement;
 
end;
 
end;
 
+
 
procedure Assign;
 
procedure Assign;
 
begin
 
begin
 
   NextLexem; // Пропуск id
 
   NextLexem; // Пропуск id
   if LexKind in [lexAssign..lexAssignMult] then
+
   if LexKind = Tok.ASSIGN then
 
     NextLexem
 
     NextLexem
 
   else syntaxerror('Ожидалось :=');   
 
   else syntaxerror('Ожидалось :=');   
 
   Expr;   
 
   Expr;   
 
end;
 
end;
 
+
 
procedure StatementList;
 
procedure StatementList;
 
begin
 
begin
 
   Statement;
 
   Statement;
   while LexKind=lexSemicolon do
+
   while LexKind=Tok.SEMICOLON do
 
   begin
 
   begin
 
     NextLexem;
 
     NextLexem;
Строка 127: Строка 127:
 
   end
 
   end
 
end;
 
end;
 
+
procedure Expr;
+
procedure Expr; // Выражение - это id или num
 
begin
 
begin
   if LexKind in [lexId,lexNum] then
+
   if LexKind in [Tok.ID,Tok.INUM] then
 
     NextLexem  
 
     NextLexem  
 
   else syntaxerror('Ожидалось выражение');   
 
   else syntaxerror('Ожидалось выражение');   
 
end;
 
end;
 
+
 
procedure syntaxerror(message: string);
 
procedure syntaxerror(message: string);
 
begin
 
begin
Строка 151: Строка 151:
 
====Основная программа====
 
====Основная программа====
 
<source lang="Delphi">uses SimpleLangLexer,SimpleLangParser;
 
<source lang="Delphi">uses SimpleLangLexer,SimpleLangParser;
 
+
 
begin  
 
begin  
 
   Init('progr.txt');
 
   Init('progr.txt');
 
   Progr;
 
   Progr;
   if LexKind=lexEot then
+
   if LexKind=Tok.EOF then
 
     writeln('программа распознана правильно ')
 
     writeln('программа распознана правильно ')
 
   else syntaxerror('ожидался конец файла');  
 
   else syntaxerror('ожидался конец файла');  
 
end.</source>
 
end.</source>
  
====Задания====
+
====Основные задания (7 баллов)====
# Добавить цикл for
+
# Добавить оператор WHILE expr DO statement(в лексический анализатор - добавлять новые ключевые слова) '''(3 балла)'''
# Добавить простые логические выражения вида x<0 (в лексический анализатор придется добавлять знаки отношений, а в синтаксический - правила грамматики
+
# Добавить оператор FOR ID := expr TO expr DO statement(в лексический анализатор - добавлять новые ключевые слова) '''(4 балла)'''
 
 
logicalexpr ::= id_or_num relop id_or_num
 
id_or_num ::= id | num
 
  
# Добавить оператор if (предусмотреть полную и неполную форму)
+
====Дополнительные задания (7 баллов)====
# Добавить оператор while
+
# Добавить оператор IF expr THEN stat ELSE stat (предусмотреть полную и неполную форму оператора) (3 балла)
# Добавить грамматику выражений
+
# Добавить грамматику выражений (4 балла)
 
  E ::= T A
 
  E ::= T A
 
  A ::= ε | + T A | - T A
 
  A ::= ε | + T A | - T A

Текущая версия на 14:32, 22 октября 2015

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

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

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

Грамматика нашего языка имеет вид:

 progr -> block 
 stlist -> statement ; stlist | statement
 statement -> assign | block | cycl
 assign -> ID := expr
 expr -> ID | INUM
 block -> BEGIN stlist END
 cycl -> CYCLE expr st   

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

 stlist -> statement ; stlist | statement

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

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

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
Tok.&Begin: Block; 
Tok.Cycle: Cycle;
Tok.Id: Assign;
else syntaxerror('Ожидался оператор');
  end;
end;
 
procedure Block;
begin
  NextLexem; // Пропуск begin
  StatementList;
  if LexKind=Tok.&End then
    NextLexem
  else syntaxerror('Ожидалось end');    
end;
 
procedure Cycle;
begin
  NextLexem; // Пропуск cycle
  Expr;
  Statement;
end;
 
procedure Assign;
begin
  NextLexem; // Пропуск id
  if LexKind = Tok.ASSIGN then
    NextLexem
  else syntaxerror('Ожидалось :=');  
  Expr;  
end;
 
procedure StatementList;
begin
  Statement;
  while LexKind=Tok.SEMICOLON do
  begin
    NextLexem;
    Statement;
  end
end;
 
procedure Expr; // Выражение - это id или num
begin
  if LexKind in [Tok.ID,Tok.INUM] 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=Tok.EOF then
    writeln('программа распознана правильно ')
  else syntaxerror('ожидался конец файла'); 
end.

Основные задания (7 баллов)

  1. Добавить оператор WHILE expr DO statement(в лексический анализатор - добавлять новые ключевые слова) (3 балла)
  2. Добавить оператор FOR ID := expr TO expr DO statement(в лексический анализатор - добавлять новые ключевые слова) (4 балла)

Дополнительные задания (7 баллов)

  1. Добавить оператор IF expr THEN stat ELSE stat (предусмотреть полную и неполную форму оператора) (3 балла)
  2. Добавить грамматику выражений (4 балла)
E ::= T A
A ::= ε | + T A | - T A
T ::= M B
B ::= ε | * M B | / M B
M ::= id | num | (E)

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