Создание лексического анализатора простого языка программирования — различия между версиями
Материал из Вики ИТ мехмата ЮФУ
Admin (обсуждение | вклад) (→Модуль лексического анализатора) |
Admin (обсуждение | вклад) (→Дополнительные задания (10 баллов - каждое по 2 балла)) |
||
(не показаны 54 промежуточные версии этого же участника) | |||
Строка 1: | Строка 1: | ||
[[Страница_курса_%22Методы_построения_компиляторов%22| К основной странице курса]] | [[Страница_курса_%22Методы_построения_компиляторов%22| К основной странице курса]] | ||
− | ==== Модуль лексического анализатора | + | === Предварительные замечания === |
+ | ==== Лексемы языка: ==== | ||
+ | ; := id num begin end cycle | ||
+ | |||
+ | Здесь | ||
+ | id - идентификатор | ||
+ | num - целое без знака | ||
+ | begin end cycle - ключевые слова | ||
+ | |||
+ | ==== Глобальные переменные, необходимые для работы лексического анализатора: ==== | ||
+ | fname: string; // Имя файла программы | ||
+ | LexRow,LexCol: integer; // Строка-столбец начала лексемы. Конец лексемы = LexCol+Length(LexText) | ||
+ | LexKind: Tok; // Тип лексемы | ||
+ | LexText: string; // Текст лексемы | ||
+ | LexValue: integer; // Целое значение, связанное с лексемой lexNum | ||
+ | |||
+ | Тип Tok является перечислимым и содержит все возможные лексемы нашего языка, а также специальную лексему EOFF конца текста: | ||
+ | Tok = (EOF, ID, INUM, COLON, SEMICOLON, ASSIGN, &BEGIN, &END, CYCLE); | ||
+ | |||
+ | ==== Вспомогательные глобальные переменные, описанные в секции реализации модуля: ==== | ||
+ | ch: Char; // Текущий символ | ||
+ | f: text; // Текущий файл | ||
+ | row,col: integer; // Текущие строка и столбец в файле | ||
+ | KeywordsMap := new Dictionary<string,TLex>; // Словарь, сопоставляющий ключевым словам константы типа TLex. | ||
+ | // Инициализируется процедурой InitKeywords | ||
+ | |||
+ | ==== Процедура NextCh ==== | ||
+ | Помимо считывания следующего символа ch, процедура NextCh поддерживает в актуальном состоянии текущие строку-столбец (row,col). При достижении конца файла в переменную ch возвращается специальный символ #0 | ||
+ | |||
+ | ==== Процедура InitKeywords ==== | ||
+ | Процедура InitKeywords инициализирует словарь ключевых слов KeywordsMap. Этот словарь ставит в соответствие строке ключевого слова константу типа TLex: | ||
+ | KeywordsMap['begin'] := lexBegin; | ||
+ | |||
+ | === Модуль лексического анализатора === | ||
<source lang="Delphi"> | <source lang="Delphi"> | ||
Строка 8: | Строка 41: | ||
Лексемы: | Лексемы: | ||
; := += -= *= id num begin end cycle | ; := += -= *= id num begin end cycle | ||
− | + | ||
Ключевые слова: | Ключевые слова: | ||
begin end cycle | begin end cycle | ||
} | } | ||
− | unit | + | unit SimpleLangLexer; |
− | + | ||
interface | interface | ||
− | + | ||
// TLex - перечислимый тип - все лексемы грамматики | // TLex - перечислимый тип - все лексемы грамматики | ||
// lexEot - конец текста программы | // lexEot - конец текста программы | ||
type | type | ||
− | + | Tok = (EOF, ID, INUM, COLON, SEMICOLON, ASSIGN, &BEGIN, &END, CYCLE); | |
− | + | ||
var | var | ||
− | fname: string; // Имя файла | + | fname: string; // Имя файла программы |
LexRow,LexCol: integer; // Строка-столбец начала лексемы. Конец лексемы = LexCol+Length(LexText) | LexRow,LexCol: integer; // Строка-столбец начала лексемы. Конец лексемы = LexCol+Length(LexText) | ||
− | + | LexKind: Tok; // Тип лексемы | |
LexText: string; // Текст лексемы | LexText: string; // Текст лексемы | ||
LexValue: integer; // Целое значение, связанное с лексемой lexNum | LexValue: integer; // Целое значение, связанное с лексемой lexNum | ||
− | + | ||
− | |||
− | |||
procedure NextLexem; | procedure NextLexem; | ||
procedure Init(fn: string); | procedure Init(fn: string); | ||
procedure Done; | procedure Done; | ||
− | + | function TokToString(t: Tok): string; | |
− | + | ||
implementation | implementation | ||
− | + | ||
− | |||
− | |||
var | var | ||
ch: Char; // Текущий символ | ch: Char; // Текущий символ | ||
f: text; // Текущий файл | f: text; // Текущий файл | ||
row,col: integer; // Текущие строка и столбец в файле | row,col: integer; // Текущие строка и столбец в файле | ||
− | KeywordsMap := new Dictionary<string, | + | KeywordsMap := new Dictionary<string,Tok>; // Словарь, сопоставляющий ключевым словам константы типа TLex. Инициализируется процедурой InitKeywords |
+ | |||
− | procedure | + | procedure LexError(message: string); // ошибка лексического анализатора |
begin | begin | ||
var ss := System.IO.File.ReadLines(fname).Skip(row-1).First(); // Строка row файла | var ss := System.IO.File.ReadLines(fname).Skip(row-1).First(); // Строка row файла | ||
Строка 53: | Строка 83: | ||
if message<>'' then | if message<>'' then | ||
writeln(message); | writeln(message); | ||
− | Done; | + | Done; |
halt; | halt; | ||
end; | end; | ||
− | + | ||
procedure NextCh; | procedure NextCh; | ||
begin | begin | ||
// В LexText накапливается предыдущий символ и считывается следующий символ | // В LexText накапливается предыдущий символ и считывается следующий символ | ||
LexText += ch; | LexText += ch; | ||
− | if not | + | if not f.Eof then |
begin | begin | ||
read(f,ch); | read(f,ch); | ||
Строка 72: | Строка 102: | ||
end; | end; | ||
end | end | ||
− | else ch := #0; | + | else |
+ | begin | ||
+ | ch := #0; // если достигнут конец файла, то возвращается #0 | ||
+ | Done; | ||
+ | end; | ||
end; | end; | ||
− | + | ||
procedure PassSpaces; | procedure PassSpaces; | ||
begin | begin | ||
Строка 80: | Строка 114: | ||
NextCh; | NextCh; | ||
end; | end; | ||
− | + | ||
procedure NextLexem; | procedure NextLexem; | ||
begin | begin | ||
PassSpaces; | PassSpaces; | ||
+ | // R К этому моменту первый символ лексемы считан в ch | ||
LexText := ''; | LexText := ''; | ||
LexRow := Row; | LexRow := Row; | ||
Строка 92: | Строка 127: | ||
';': begin | ';': begin | ||
NextCh; | NextCh; | ||
− | + | LexKind := Tok.SEMICOLON; | |
end; | end; | ||
':': begin | ':': begin | ||
Строка 99: | Строка 134: | ||
lexerror('= ожидалось'); | lexerror('= ожидалось'); | ||
NextCh; | NextCh; | ||
− | + | LexKind := Tok.ASSIGN; | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end; | end; | ||
'a'..'z': begin | 'a'..'z': begin | ||
Строка 126: | Строка 140: | ||
NextCh; | NextCh; | ||
if KeywordsMap.ContainsKey(LexText) then | if KeywordsMap.ContainsKey(LexText) then | ||
− | + | LexKind := KeywordsMap[LexText] | |
− | else | + | else LexKind := Tok.ID; |
end; | end; | ||
'0'..'9': begin | '0'..'9': begin | ||
− | while ch | + | while char.IsDigit(ch) do |
NextCh; | NextCh; | ||
LexValue := integer.Parse(LexText); | LexValue := integer.Parse(LexText); | ||
− | + | LexKind := Tok.INUM; | |
end; | end; | ||
− | #0: | + | #0: LexKind := Tok.EOF; |
else lexerror('Неверный символ '+ch); | else lexerror('Неверный символ '+ch); | ||
end; | end; | ||
end; | end; | ||
− | + | ||
procedure InitKeywords; | procedure InitKeywords; | ||
begin | begin | ||
− | KeywordsMap['begin'] := | + | KeywordsMap['begin'] := Tok.&BEGIN; |
− | KeywordsMap['end'] := | + | KeywordsMap['end'] := Tok.&END; |
− | KeywordsMap['cycle'] := | + | KeywordsMap['cycle'] := Tok.CYCLE; |
end; | end; | ||
− | + | ||
procedure Init(fn: string); | procedure Init(fn: string); | ||
begin | begin | ||
InitKeywords; | InitKeywords; | ||
fname := fn; | fname := fn; | ||
− | + | AssignFile(f,fname); | |
reset(f); | reset(f); | ||
row := 1; col := 1; | row := 1; col := 1; | ||
− | NextCh; | + | NextCh; // Считать первый символ в ch |
− | NextLexem; | + | NextLexem; // Считать первую лексему, заполнив LexText, LexKind и, возможно, LexValue |
end; | end; | ||
− | + | ||
procedure Done; | procedure Done; | ||
begin | begin | ||
close(f); | close(f); | ||
+ | end; | ||
+ | |||
+ | function TokToString(t: Tok): string; | ||
+ | begin | ||
+ | Result := t.ToString; | ||
+ | case t of | ||
+ | Tok.ID: Result += ' ' + LexText; | ||
+ | Tok.INUM: Result += ' ' + LexValue; | ||
+ | end; | ||
end; | end; | ||
− | end. | + | end.</source> |
+ | |||
+ | === Основная программа === | ||
+ | <source lang="Pascal"> | ||
+ | uses SimpleLangLexer; | ||
+ | |||
+ | begin | ||
+ | Init('a.txt'); | ||
+ | repeat | ||
+ | writeln(TokToString(LexKind)); | ||
+ | NextLexem; | ||
+ | until LexKind = Tok.EOF; | ||
+ | end.</source> | ||
+ | |||
+ | === Основное задание === | ||
+ | Разобраться в программе. Откомпилировать программу (0 баллов) | ||
− | </ | + | === Дополнительные задания (10 баллов - каждое по 2 балла)=== |
+ | # Добавить распознавание лексем , : + - * / div mod and or not | ||
+ | # Добавить распознавание лексем += -= *= /=. Тщательно продумать, как совместить распознавание лексем с одинаковым префиксом: например, + и += | ||
+ | # Добавить распознавание лексем > < >= <= = <> | ||
+ | # Добавить пропуск комментариев // - до конца строки | ||
+ | # Добавить пропуск комментариев { комментарий до закрывающей фигурной скобки }. Обратить внимание, что незакрытый до конца файла комментарий - это синтаксическая ошибка |
Текущая версия на 12:33, 22 декабря 2016
Содержание
Предварительные замечания
Лексемы языка:
; := id num begin end cycle
Здесь
id - идентификатор num - целое без знака begin end cycle - ключевые слова
Глобальные переменные, необходимые для работы лексического анализатора:
fname: string; // Имя файла программы LexRow,LexCol: integer; // Строка-столбец начала лексемы. Конец лексемы = LexCol+Length(LexText) LexKind: Tok; // Тип лексемы LexText: string; // Текст лексемы LexValue: integer; // Целое значение, связанное с лексемой lexNum
Тип Tok является перечислимым и содержит все возможные лексемы нашего языка, а также специальную лексему EOFF конца текста:
Tok = (EOF, ID, INUM, COLON, SEMICOLON, ASSIGN, &BEGIN, &END, CYCLE);
Вспомогательные глобальные переменные, описанные в секции реализации модуля:
ch: Char; // Текущий символ f: text; // Текущий файл row,col: integer; // Текущие строка и столбец в файле KeywordsMap := new Dictionary<string,TLex>; // Словарь, сопоставляющий ключевым словам константы типа TLex. // Инициализируется процедурой InitKeywords
Процедура NextCh
Помимо считывания следующего символа ch, процедура NextCh поддерживает в актуальном состоянии текущие строку-столбец (row,col). При достижении конца файла в переменную ch возвращается специальный символ #0
Процедура InitKeywords
Процедура InitKeywords инициализирует словарь ключевых слов KeywordsMap. Этот словарь ставит в соответствие строке ключевого слова константу типа TLex:
KeywordsMap['begin'] := lexBegin;
Модуль лексического анализатора
// Распознавание программы на простом языке. Распознаватель лексем (лексер)
{
Лексемы:
; := += -= *= id num begin end cycle
Ключевые слова:
begin end cycle
}
unit SimpleLangLexer;
interface
// TLex - перечислимый тип - все лексемы грамматики
// lexEot - конец текста программы
type
Tok = (EOF, ID, INUM, COLON, SEMICOLON, ASSIGN, &BEGIN, &END, CYCLE);
var
fname: string; // Имя файла программы
LexRow,LexCol: integer; // Строка-столбец начала лексемы. Конец лексемы = LexCol+Length(LexText)
LexKind: Tok; // Тип лексемы
LexText: string; // Текст лексемы
LexValue: integer; // Целое значение, связанное с лексемой lexNum
procedure NextLexem;
procedure Init(fn: string);
procedure Done;
function TokToString(t: Tok): string;
implementation
var
ch: Char; // Текущий символ
f: text; // Текущий файл
row,col: integer; // Текущие строка и столбец в файле
KeywordsMap := new Dictionary<string,Tok>; // Словарь, сопоставляющий ключевым словам константы типа TLex. Инициализируется процедурой InitKeywords
procedure LexError(message: string); // ошибка лексического анализатора
begin
var ss := System.IO.File.ReadLines(fname).Skip(row-1).First(); // Строка row файла
writeln('Лексическая ошибка в строке ',row,':');
writeln(ss);
writeln('^':col-1);
if message<>'' then
writeln(message);
Done;
halt;
end;
procedure NextCh;
begin
// В LexText накапливается предыдущий символ и считывается следующий символ
LexText += ch;
if not f.Eof then
begin
read(f,ch);
if ch<>#10 then
col += 1
else
begin
row += 1;
col := 1;
end;
end
else
begin
ch := #0; // если достигнут конец файла, то возвращается #0
Done;
end;
end;
procedure PassSpaces;
begin
while char.IsWhiteSpace(ch) do
NextCh;
end;
procedure NextLexem;
begin
PassSpaces;
// R К этому моменту первый символ лексемы считан в ch
LexText := '';
LexRow := Row;
LexCol := Col;
// Тип лексемы определяется по ее первому символу
// Для каждой лексемы строится синтаксическая диаграмма
case ch of
';': begin
NextCh;
LexKind := Tok.SEMICOLON;
end;
':': begin
NextCh;
if ch<>'=' then
lexerror('= ожидалось');
NextCh;
LexKind := Tok.ASSIGN;
end;
'a'..'z': begin
while ch in ['a'..'z','0'..'9'] do
NextCh;
if KeywordsMap.ContainsKey(LexText) then
LexKind := KeywordsMap[LexText]
else LexKind := Tok.ID;
end;
'0'..'9': begin
while char.IsDigit(ch) do
NextCh;
LexValue := integer.Parse(LexText);
LexKind := Tok.INUM;
end;
#0: LexKind := Tok.EOF;
else lexerror('Неверный символ '+ch);
end;
end;
procedure InitKeywords;
begin
KeywordsMap['begin'] := Tok.&BEGIN;
KeywordsMap['end'] := Tok.&END;
KeywordsMap['cycle'] := Tok.CYCLE;
end;
procedure Init(fn: string);
begin
InitKeywords;
fname := fn;
AssignFile(f,fname);
reset(f);
row := 1; col := 1;
NextCh; // Считать первый символ в ch
NextLexem; // Считать первую лексему, заполнив LexText, LexKind и, возможно, LexValue
end;
procedure Done;
begin
close(f);
end;
function TokToString(t: Tok): string;
begin
Result := t.ToString;
case t of
Tok.ID: Result += ' ' + LexText;
Tok.INUM: Result += ' ' + LexValue;
end;
end;
end.
Основная программа
uses SimpleLangLexer;
begin
Init('a.txt');
repeat
writeln(TokToString(LexKind));
NextLexem;
until LexKind = Tok.EOF;
end.
Основное задание
Разобраться в программе. Откомпилировать программу (0 баллов)
Дополнительные задания (10 баллов - каждое по 2 балла)
- Добавить распознавание лексем , : + - * / div mod and or not
- Добавить распознавание лексем += -= *= /=. Тщательно продумать, как совместить распознавание лексем с одинаковым префиксом: например, + и +=
- Добавить распознавание лексем > < >= <= = <>
- Добавить пропуск комментариев // - до конца строки
- Добавить пропуск комментариев { комментарий до закрывающей фигурной скобки }. Обратить внимание, что незакрытый до конца файла комментарий - это синтаксическая ошибка