А. Григорьев - О чём не пишут в книгах по Delphi
- Название:О чём не пишут в книгах по Delphi
- Автор:
- Жанр:
- Издательство:БХВ-Петербург
- Год:2008
- Город:СПб
- ISBN:978-5-9775-019003
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
А. Григорьев - О чём не пишут в книгах по Delphi краткое содержание
Рассмотрены малоосвещённые вопросы программирования в Delphi. Описаны методы интеграции VCL и API. Показаны внутренние механизмы VCL и приведены примеры вмешательства в эти механизмы. Рассмотрено использование сокетов в Delphi: различные механизмы их работы, особенности для протоколов TCP и UDP и др. Большое внимание уделено разбору ситуаций возникновения ошибок и получения неверных результатов в "простом и правильном" коде. Отдельно рассмотрены особенности работы с целыми, вещественными и строковыми типами данных, а также приведены примеры неверных результатов, связанных с ошибками компилятора, VCL и др. Для каждой из таких ситуаций предложены методы решения проблемы. Подробно рассмотрен синтаксический анализ в Delphi на примере арифметических выражений. Многочисленные примеры составлены с учётом различных версий: от Delphi 3 до Delphi 2007. Прилагаемый компакт-диск содержит примеры из книги.
Для программистов
О чём не пишут в книгах по Delphi - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
Чтобы продемонстрировать возможности лексического анализатора, добавим поддержку комментариев. Комментарий — это последовательность символов, начинающаяся с "{" и заканчивающаяся "}", которая может содержать внутри себя любые символы, кроме "}". Комментарий считается разделителем, он допустим в любом месте, где возможно появление других разделителей, т.е. в начале и в конце строки и между лексемами.
Пример калькулятора с лексическим анализатором также находится на компакт-диске и называется LexicalSample.
Лексический анализатор на входе получает строку, на выходе он должен дать список структур, каждая из которых описывает одну лексему. В нашем примере эти структуры выглядят следующим образом (листинг 4.11).
TLexeme
для хранения информации об одной лексемеTLexemeType = (
ltEqual, ltLess, ltGreater, ltLessOrEqual,
ltGreaterOrEqual, ltNotEqual, ltPlus, ltMinus,
ltOr, ltXor, ltAsterisk, ltSlash, ltDiv, ltMod,
ltAnd, ltNot, ltCap,
ltLeftBracket, ltRightBracket,
ltSin, ltCos, ltLn,
ltIdentifier, ltNumber, ltEnd);
TLexeme = record
LexemeType: TLexemeType;
Pos: Integer;
Lexeme: string;
end;
LexemeType
— поле, содержащее информацию о том, что это за лексема. Тип TLexemeType
— это специально определенный перечислимый тип, каждое из значений которого соответствует одному из возможных типов лексемы. Поле Pos
хранит номер позиции в строке, начиная с которой идет данная лексема. Это поле нужно только для того, чтобы синтаксический анализатор мог точно указать место ошибки, если встретит недопустимую лексему.
Поле Lexeme
хранит саму подстроку, распознанную как лексема. Оно используется, только если тип лексемы равен ltIdentifier
или ltNumber
. Для остальных типов лексем достаточно информации из поля LexemeType
.
Лексический анализатор реализован в виде класса TLexicalAnalyzer
. В конструкторе класса выполняется разбор строки и формирование списка лексем. Через этот же класс синтаксический анализатор получает доступ к лексемам: свойство Lexeme
возвращает текущую лексему, метод Next
позволяет перейти к следующей. Так как наша грамматика предусматривает разбор слева направо, таких примитивных возможностей навигации синтаксическому анализатору вполне хватает. Код анализатора показан в листинге 4.12.
type
TLexicalAnalyzer = class
private
FLexemeList: TList;
// Номер текущей лексемы в списке
FIndex: Integer;
function GetLexeme: PLexeme;
// Пропуск всего, что является разделителем лексем
procedure SkipWhiteSpace(const S: string; var P: Integer);
// Выделение лексемы, начинающейся с позиции P
procedure ExtractLexeme(const S: string; var P: Integer);
// Помещение лексемы в список
procedure PutLexeme(LexemeType: TLexemeType; Pos: Integer; const Lexeme: string);
// Выделение лексемы-числа
procedure Number(const S: string; var P: Integer);
// Выделение слова и отнесение его к идентификаторам
// или зарезервированным словам
procedure Word(const S: string; var P: Integer);
public
constructor Create(const Expr: string);
destructor Destroy; override;
// Переход к следующей лексеме
procedure Next;
// Указатель на текущую лексему
property Lexeme: PLexeme read GetLexeme;
end;
constructor TLexicalAnalyzer.Create(const Expr: string);
var
P: Integer;
begin
inherited Create;
// Создаем список лексем
FLexemeList := TList.Create;
// И сразу же заполняем его
Р := 1;
while Р <= Length(Expr) do
begin
SkipWhiteSpace(Expr, P);
ExtractLexeme(Expr, P);
end;
// Помещаем в конец списка специальную лексему
PutLexeme(ltEnd, Р, '');
FIndex := 0;
end;
destructor TLexicalAnalyzer.Destroy;
var
I: Integer;
begin
for I := 0 to FLexemeList.Count - 1 do
Dispose(PLexeme(FLexemeList[I]));
FLexemeList.Free;
inherited Destroy;
end;
// Получение указателя на текущую лексему
function TLexicalAnalyzer.GetLexeme: PLexeme;
begin
Result := FLexemeList[FIndex];
end;
// Переход к следующей лексеме
procedure TLexicalAnalyzer.Next;
begin
if FIndex < FLexemeList.Count - 1 then Inc(FIndex);
end;
// Помещение лексемы в список. Параметры метода задают
// одноименные поля типа TLexeme.
procedure TLexicalAnalyzer.PutLexeme(LexemeType: TLexemeType; Pos: Integer; const Lexeme: string);
var
NewLexeme: PLexeme;
begin
New(NewLexeme);
NewLexeme^.LexemeType := LexemeType;
NewLexeme^.Pos := Pos;
NewLexeme^.Lexeme := Lexeme;
FLexemeList.Add(NewLexeme);
end;
// пропускает пробелы, символы табуляции, комментарии и переводы строки,
// которые могут находиться в начале и в конце строки и между лексемами
procedure TLexicalAnalyzer.SkipWhiteSpace(const S: string; var P: Integer);
begin
while (P <= Length(S)) and (S[P] in [' ', #9, #13, #10, '{']) do
if S[P] = '{' then
begin
Inc(P);
while (P <-=Length(S)) and (S[P) <> '}') do Inc(P);
if P > Length(S) then
raise ESyntaxError.Create('Незавершенный комментарий');
Inc(P);
end
else Inc(P);
end;
// Функция выделяет одну лексему и помещает ее в список
procedure TLexicalAnalyzer.ExtractLexeme(const S: string; var P: Integer);
begin
if P > Length(S) then Exit;
case S[P] of
'(': begin
PutLexeme(ltLeftBracket, P, '');
Inc(P);
end;
')': begin
PutLexeme(ltRightBracket, P, '');
Inc(P);
end;
'*': begin
PutLexeme(ltAsterisk, P, '');
Inc(P);
end;
'+': begin
PutLexeme(ltPlus, P, '');
Inc(P);
end;
'-': begin
PutLexeme(ltMinus, P, '');
Inc(P);
end;
'/': begin
PutLexeme(ltSlash, P, '');
Inc(P);
end;
'0'..'9': Number(S, P);
'<':if (P < Length(S)) and (S[P + 1] = '=') then
begin
PutLexeme(ltLessOrEqual, P, '');
Inc(P, 2);
end
else
if (P < Length(S)) and (S[P + 1] = '>') then
begin
PutLexeme(ltNotEqual, P, '');
Inc(P, 2);
end
else
begin
PutLexeme(ltLess, P, '');
Inc(P);
end;
'=': begin
PutLexeme(ltEqual, P, '');
Inc(P);
end;
'>': if (P < Length(S)) and (S[P + 1] = '=') then
begin
PutLexeme(ltGreaterOrEqual, P, '');
Inc(P, 2);
end
else
begin
PutLexeme(ltGreater, P, '');
Inc(P);
end;
'A'..'Z, 'a'..'z', '_': Word(S, P);
'^': begin
PutLexeme(ltCap, P, '');
Inc(P);
end;
else
raise ESyntaxError.Create('Некорректный символ в позиции ' +
IntToStr(Р));
end;
end;
// Выделение лексемы-числа
procedure TLexicalAnalyzer.Number(const S: string; var P: Integer);
var
InitPos, RollbackPos: Integer;
function IsDigit(Ch: Char): Boolean;
begin
Интервал:
Закладка: