Spec-Zone.ru › Perl 5.36

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 действительно создаёт строки Unicode.

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" );

И, наконец, проверим, что мы не создаём строки Unicode, если 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–2021 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.36.0/perlhacktut

Spec-Zone.ru

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