Главная » Статьи » FreePascal

Ошибка в классе TPascalScanner

Для подготовки справочной документации для модулей в состав пакета FreePascal входят две утилиты:

  • • MAKESKEL, генерирующая по файлу исходного кода модуля пустой XML–файл описания, который в дальнейшем может быть отредактирован с помощью LazDE, например;
  • • FPDOC — это инструмент, который объединяет файл модуля Pascal и файл описания в формате XML и создает справочную документацию для модуля в выбранном формате.

И вот при использовании этих утилит я обнаружил, что две одинаковых директивы IfDef условной компиляции:

{$Define def1}

{$IfDef def1}
procedure p1;
{$EndIf}

{$IfDef def1 }
procedure p2;
{$EndIf}

обрабатываются этими утилитами по разному: для первой директивы блок кода до директивы EndIf — обрабатывался, для второй — пропускался. Различие между директивами было в том, что во втором случае между идентификатором def1 и закрывающейся скобкой } был расположен пробел. Такое же различие в работе наблюдалось и для директивы IfNDef. При этом компилятор FPC обрабатывал абсолютно одинаково в полном соответствии с описанием, которое допускает наличие комментария между условным идентификатором (именем) и скобкой, например, {$ifdef def1 this is a comment}.

Из исходных файлов MAKESKEL и  FPDOC было видно, что для парсинга исходного кода эти утилиты используют модули пакета fcl-passrcPascal parsing parts of Free Component Libraries (FCL). Конкретно, для лексического анализа исходного кода Pascal применяется класс TPascalScanner, реализованный в файле pscanner.pp.

Поиск причины некорректной обработки был облегчен тем фактом, что директива Define корректно отрабатывала и при наличии комментария между условным идентификатором и скобкой }.

В классе TPascalScanner парсинг директив Define, IfDef и IfNDef выполняется в следующих методах: HandleDefine, HandleIFDEF и HandleIFNDEF, которым передается в виде параметра весь остаток строки, начиная с условного идентификатора до скобки }. Эти методы имеют следующий код:

2487   procedure TPascalScanner.HandleDefine(Param: String);
2488
2489   Var
2490     Index : Integer;
2491     MName,MValue : String;
2492
2493   begin
2494     Param := UpperCase(Param);
2495     Index:=Pos(':=',Param);
2496     If (Index=0) then
2497       AddDefine(GetMacroName(Param))  //добавление условного имени
2498     else
           ...
2505   end;

2630   procedure TPascalScanner.HandleIFDEF(const AParam: String);
2631   begin
2632     PushSkipMode;
2633     if PPIsSkipping then
2634       PPSkipMode := ppSkipAll
2635     else
2636       begin
2637       if IsDefined(AParam) then    //проверка определения условного имени
2638         PPSkipMode := ppSkipElseBranch
2639       else
2640         begin
2641         PPSkipMode := ppSkipIfBranch;
2642         PPIsSkipping := true;
2643         end;
           ...
2649       end;
2650   end;

2652   procedure TPascalScanner.HandleIFNDEF(const AParam: String);
2653   begin
2654     PushSkipMode;
2655     if PPIsSkipping then
2656       PPSkipMode := ppSkipAll
2657     else
2658       begin
2659       if IsDefined(AParam) then    //проверка определения условного имени
2660         begin
2661         PPSkipMode := ppSkipIfBranch;
2662         PPIsSkipping := true;
2663         end
2664       else
2665         PPSkipMode := ppSkipElseBranch;
           ...
2671       end;
2672   end;

Отсюда видно, что при добавлении условного имени параметр, в котором передается методу HandleDefine весь остаток строки, начиная с условного идентификатора до скобки }, предварительно обрабатывается функцией GetMacroName. Исходный код этой функции имеет следующий вид:

3527   function TPascalScanner.GetMacroName(const Param: String): String;
3528   var
3529     p: Integer;
3530   begin
3531     Result:=Param;
3532     p:=1;
3533     while (p<=length(Param)) and (Param[p] in ['a'..'z','A'..'Z','0'..'9','_']) do
3534       inc(p);
3535     SetLength(Result,p-1);
3536   end;

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

В методах же HandleIFDEF и HandleIFNDEF для проверки определено ли условное имя функции IsDefined передается исходный параметр, который может содержать дополнительные символы после имени.

После коррекции строк 2637 и 2659 в методах HandleIFDEF и HandleIFNDEF, соответственно, следующим образом:
if IsDefined(GetMacroName(AParam)) then
и перекомпиляции утилит MAKESKEL и  FPDOC обработка директив IfDef и IfNDef стала выполняться корректно.

Категория: FreePascal | Добавил: zoleg5763 (03.02.2019)
Просмотров: 184 | Комментарии: 1 | Рейтинг: 0.0/0
Всего комментариев: 1
avatar
0
1 zoleg5763 • 10:05, 08.09.2019
Ошибка исправлена в r40582.
avatar