Spec-Zone.ru › Perl 5.28

perlhacktut

СОДЕРЖАНИЕ

  • ИМЯ
  • ОПИСАНИЕ
  • ПРИМЕР ПРОСТОГО ПАТЧА
    • Написание патча
    • Тестирование патча
    • Документирование патча
    • Отправить
  • АВТОР

ИМЯ

perlhacktut - пошаговое руководство по созданию простого патча для кода на C

ОПИСАНИЕ

Этот документ содержит пример простого патча.

Если вы еще не читали perlhack, сделайте это в первую очередь! Также рекомендуется ознакомиться с perlsource.

После этого перейдите к perlhacktips.

ПРИМЕР ПРОСТОГО ПАТЧА

Рассмотрим простой патч от начала до конца.

Вот что предложил Ларри: если U — это первый активный формат во время pack, (например, pack "U3C8", @stuff), то результирующая строка должна обрабатываться как закодированная в UTF-8.

Если вы работаете с клоном репозитория Perl с помощью git, вы должны создать ветку для своих изменений. Это значительно упростит создание корректного патча. Подробную информацию об этом см. в perlgit.

Написание патча

Как подготовиться к исправлению? Сначала найдем нужный код — pack выполняется во время выполнения, поэтому он будет в одном из файлов pp. Действительно, pp_pack находится в pp.c. Поскольку мы будем изменять этот файл, скопируем его в pp.c~.

[В момент написания данного руководства он находился в pp.c. В настоящее время он вынесен с pp_unpack в отдельный файл pp_pack.c]

Теперь рассмотрим pp_pack: мы применяем шаблон к pat, а затем перебираем шаблон, обрабатывая каждый символ формата поочередно в datum_type. Затем для каждого возможного символа формата мы пропускаем остальные аргументы в шаблоне (ширина поля, звёздочка и т. д.) и преобразуем следующий фрагмент ввода в указанный формат, добавляя его к выходному SV cat.

Как узнать, является ли U первым форматом в pat? Если у нас есть указатель на начало pat, то если мы видим U, мы можем проверить, находимся ли мы все еще в начале строки. Вот где настраивается pat:

STRLEN fromlen;
char *pat = SvPVx(*++MARK, fromlen);
char *patend = pat + fromlen;
I32 len;
I32 datumtype;
SV *fromstr;

У нас будет еще один указатель на строку:

   STRLEN fromlen;
   char *pat = SvPVx(*++MARK, fromlen);
   char *patend = pat + fromlen;
+  char *patcopy;
   I32 len;
   I32 datumtype;
   SV *fromstr;

И сразу перед началом цикла мы установим patcopy в начало pat:

   items = SP - MARK;
   MARK++;
   SvPVCLEAR(cat);
+  patcopy = pat;
   while (pat < patend) {

Теперь, если мы увидим U в начале строки, мы включим флаг UTF8 для выходного SV, cat:

+  if (datumtype == 'U' && pat==patcopy+1)
+      SvUTF8_on(cat);
   if (datumtype == '#') {
       while (pat < patend && *pat != '\n')
           pat++;

Помните, что это должно быть patcopy+1, потому что первый символ строки — это U, который был обработан datumtype!

О, мы забыли кое-что: а что, если в начале шаблона есть пробелы? pack(" U*", @stuff) будет иметь U в качестве первого активного символа, даже если это не первое в шаблоне. В этом случае мы должны продвинуть patcopy вместе с pat при обнаружении пробелов:

if (isSPACE(datumtype))
    continue;

должно стать

if (isSPACE(datumtype)) {
    patcopy++;
    continue;
}

Хорошо. C-часть выполнена. Теперь нам нужно сделать два дополнительных шага, прежде чем этот патч будет готов: мы изменили поведение Perl, поэтому мы должны это задокументировать. Мы также должны предоставить больше регрессионных тестов, чтобы убедиться, что наш патч работает и не создает ошибки в других частях кода.

Тестирование патча

Регрессионные тесты для каждого оператора находятся в t/op/, поэтому мы копируем t/op/pack.t в t/op/pack.t~. Теперь мы можем добавить наши тесты в конец. Сначала проверим, что U действительно создаёт строки Юникода.

В t/op/pack.t есть разумная функция ok(), но если бы ее не было, мы могли бы использовать функцию из t/test.pl.

require './test.pl';
plan( tests => 159 );

Вместо этого:

print 'not ' unless "1.20.300.4000" eq sprintf "%vd",
                                              pack("U*",1,20,300,4000);
print "ok $test\n"; $test++;

мы можем написать более разумный код (см. Test::More для полного объяснения функций is() и других функций тестирования).

is( "1.20.300.4000", sprintf "%vd", pack("U*",1,20,300,4000),
                                      "U* produces Unicode" );

Теперь проверим, что мы правильно обработали случай с пробелами в начале:

is( "1.20.300.4000", sprintf "%vd", pack("  U*",1,20,300,4000),
                                    "  with spaces at the beginning" );

И наконец, проверим, что мы не создаём строки Юникода, если U не является первым активным форматом:

isnt( v1.20.300.4000, sprintf "%vd", pack("C0U*",1,20,300,4000),
                                      "U* not first isn't Unicode" );

Необходимо изменить количество тестов вверху, иначе автоматизированный инструмент тестирования запутается. Это может выглядеть так:

print "1..156\n";

или так:

plan( tests => 156 );

Теперь мы компилируем Perl и запускаем набор тестов. Наши новые тесты пройдены, ура!

Документирование патча

Наконец, документация. Работа никогда не заканчивается до тех пор, пока не завершена документация, поэтому опишем сделанное нами изменение. Соответствующее место находится в pod/perlfunc.pod; опять же, сделаем копию, а затем вставим этот текст в описание pack:

=item *

If the pattern begins with a C<U>, the resulting string will be treated
as UTF-8-encoded Unicode. You can force UTF-8 encoding on in a string
with an initial C<U0>, and the bytes that follow will be interpreted as
Unicode characters. If you don't want this to happen, you can begin
your pattern with C<C0> (or anything else) to force Perl not to UTF-8
encode your string, and then follow this with a C<U*> somewhere in your
pattern.

Отправить

См. perlhack для получения информации об отправке патча.

АВТОР

Этот документ первоначально был написан Натаном Торкинтоном и поддерживается рассылкой perl5-porters.

© 1993–2020 Larry Wall and others
Licensed under the GNU General Public License version 1 or later, or the Artistic License.
The Perl logo is a trademark of the Perl Foundation.
https://perldoc.perl.org/5.28.3/perlhacktut

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API