Создание синтаксического анализатора простого языка программирования — различия между версиями
Admin (обсуждение | вклад) (→Модуль синтаксического анализатора (парсера)) |
Admin (обсуждение | вклад) (→Дополнительные задания (5 баллов)) |
||
(не показано 49 промежуточных версий этого же участника) | |||
Строка 1: | Строка 1: | ||
[[Страница_курса_%22Методы_построения_компиляторов%22| К основной странице курса]] | [[Страница_курса_%22Методы_построения_компиляторов%22| К основной странице курса]] | ||
+ | __NOTOC__ | ||
+ | === Комментарии к синтаксическому анализатору=== | ||
+ | Мы пишем синтаксический анализатор '''методом рекурсивного спуска'''. Для этого грамматика должна быть ''праворекурсивной''. | ||
− | ==== Модуль синтаксического анализатора (парсера) | + | Грамматика нашего языка имеет вид: |
+ | 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 | ||
+ | праворекурсивно. Следует обратить внимание, что точка с запятой разделяет операторы, а не завершает их. | ||
+ | |||
+ | Примеры правильных программ в данной грамматике: | ||
+ | |||
+ | <source lang="Delphi">begin | ||
+ | a := 2 | ||
+ | end</source> | ||
+ | |||
+ | |||
+ | |||
+ | <source lang="Pascal">begin | ||
+ | a := 2; | ||
+ | cycle a | ||
+ | begin | ||
+ | b := a; | ||
+ | c := 234 | ||
+ | end | ||
+ | end</source> | ||
+ | |||
+ | |||
+ | <source lang="Pascal"> | ||
+ | begin | ||
+ | b := 2 | ||
+ | end</source> | ||
+ | |||
+ | |||
+ | Примеры неправильных программ в данной грамматике: | ||
+ | <pre> | ||
+ | a := 2; | ||
+ | </pre> | ||
+ | |||
+ | <pre> | ||
+ | begin | ||
+ | b := 2 | ||
+ | end. | ||
+ | </pre> | ||
+ | |||
+ | При написании синтаксического анализатора методом рекурсивного спуска следует для каждого нетерминала реализовать процедуру, которая будет распознавать этот нетерминал. При реализации необходимо придерживаться следующей стратегии: перед вызовом каждой процедуры для нетерминала считается, что первая лексема уже считана с помощью NextLexem. Кроме того, каждая процедура после распознавания своей конструкции считывает следующую лексему с помощью NextLexem, подготавливая вызов следующей процедуры. | ||
+ | |||
+ | Ошибки, распознаваемые синтаксическим анализатором, называются '''синтаксическими''' и выводятся вызовом процедуры syntaxerror. | ||
+ | |||
+ | Отметим также, что приведенный парсер (синтаксический анализатор) не выполняет никаких семантических действий - он только распознает, принадлежит ли программа языку, порождаемому данной грамматикой. | ||
+ | |||
+ | === Модуль синтаксического анализатора (парсера) === | ||
<source lang="Delphi"> | <source lang="Delphi"> | ||
− | + | unit SimpleLangParser; | |
− | + | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | unit | ||
− | |||
interface | interface | ||
− | + | ||
+ | procedure Progr; | ||
procedure Expr; | procedure Expr; | ||
procedure Assign; | procedure Assign; | ||
− | procedure | + | procedure StatementList; |
− | procedure | + | procedure Statement; |
− | procedure | + | procedure Block; |
procedure Cycle; | procedure Cycle; | ||
− | procedure | + | procedure syntaxerror(message: string := ''); |
− | + | ||
− | |||
implementation | implementation | ||
− | + | ||
uses | uses | ||
− | + | SimpleLangLexer; | |
− | + | ||
− | |||
− | |||
procedure Progr; | procedure Progr; | ||
begin | begin | ||
− | + | Block | |
end; | end; | ||
− | + | ||
− | procedure | + | procedure Statement; |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
begin | begin | ||
− | case | + | case LexKind of |
− | + | Tok.&Begin: Block; | |
− | + | Tok.Cycle: Cycle; | |
− | + | Tok.Id: Assign; | |
− | else | + | else syntaxerror('Ожидался оператор'); |
end; | end; | ||
end; | end; | ||
− | + | ||
− | procedure | + | procedure Block; |
begin | begin | ||
− | NextLexem; | + | NextLexem; // Пропуск begin |
− | + | StatementList; | |
− | if | + | if LexKind=Tok.&End then |
NextLexem | NextLexem | ||
− | else | + | else syntaxerror('Ожидалось end'); |
end; | end; | ||
− | + | ||
procedure Cycle; | procedure Cycle; | ||
begin | begin | ||
− | NextLexem; | + | NextLexem; // Пропуск cycle |
Expr; | Expr; | ||
− | + | Statement; | |
end; | end; | ||
− | + | ||
procedure Assign; | procedure Assign; | ||
begin | begin | ||
− | NextLexem; | + | NextLexem; // Пропуск id |
− | if | + | if LexKind = Tok.ASSIGN then |
NextLexem | NextLexem | ||
− | else | + | else syntaxerror('Ожидалось :='); |
Expr; | Expr; | ||
end; | end; | ||
− | + | ||
− | procedure Expr; | + | procedure StatementList; |
+ | begin | ||
+ | Statement; | ||
+ | while LexKind=Tok.SEMICOLON do | ||
+ | begin | ||
+ | NextLexem; | ||
+ | Statement; | ||
+ | end | ||
+ | end; | ||
+ | |||
+ | procedure Expr; // Выражение - это id или num | ||
begin | begin | ||
− | if | + | if LexKind in [Tok.ID,Tok.INUM] then |
NextLexem | NextLexem | ||
− | else | + | else syntaxerror('Ожидалось выражение'); |
end; | end; | ||
− | + | ||
− | procedure | + | procedure syntaxerror(message: string); |
begin | begin | ||
var ss := System.IO.File.ReadLines(fname).Skip(LexRow-1).First(); // Строка row файла | var ss := System.IO.File.ReadLines(fname).Skip(LexRow-1).First(); // Строка row файла | ||
Строка 103: | Строка 147: | ||
end; | end; | ||
− | end. | + | end.</source> |
− | </source> | ||
− | |||
− | |||
− | |||
+ | ====Основная программа==== | ||
+ | <source lang="Delphi">uses SimpleLangLexer,SimpleLangParser; | ||
+ | |||
begin | begin | ||
Init('progr.txt'); | Init('progr.txt'); | ||
Progr; | Progr; | ||
− | if | + | if LexKind=Tok.EOF then |
writeln('программа распознана правильно ') | writeln('программа распознана правильно ') | ||
− | else | + | else syntaxerror('ожидался конец файла'); |
end.</source> | end.</source> | ||
− | ''' | + | ====Основные задания (7 баллов)==== |
− | # Добавить | + | # Добавить оператор WHILE expr DO statement(в лексический анализатор - добавлять новые ключевые слова) '''(3 балла)''' |
− | + | # Добавить оператор FOR ID := expr TO expr DO statement(в лексический анализатор - добавлять новые ключевые слова) '''(4 балла)''' | |
− | # Добавить грамматику выражений | + | |
+ | ====Дополнительные задания (7 баллов)==== | ||
+ | # Добавить оператор IF expr THEN stat ELSE stat (предусмотреть полную и неполную форму оператора) (3 балла) | ||
+ | # Добавить грамматику выражений (4 балла) | ||
E ::= T A | E ::= T A | ||
A ::= ε | + T A | - T A | A ::= ε | + T A | - T A | ||
Строка 127: | Строка 173: | ||
M ::= id | num | (E) | M ::= id | num | (E) | ||
Грамматика составлена так, что по ней можно составить ручной анализатор методом рекурсивного спуска. | Грамматика составлена так, что по ней можно составить ручной анализатор методом рекурсивного спуска. | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− |
Текущая версия на 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 баллов)
- Добавить оператор WHILE expr DO statement(в лексический анализатор - добавлять новые ключевые слова) (3 балла)
- Добавить оператор FOR ID := expr TO expr DO statement(в лексический анализатор - добавлять новые ключевые слова) (4 балла)
Дополнительные задания (7 баллов)
- Добавить оператор IF expr THEN stat ELSE stat (предусмотреть полную и неполную форму оператора) (3 балла)
- Добавить грамматику выражений (4 балла)
E ::= T A A ::= ε | + T A | - T A T ::= M B B ::= ε | * M B | / M B M ::= id | num | (E)
Грамматика составлена так, что по ней можно составить ручной анализатор методом рекурсивного спуска.