59. Действия (actions) в грамматиках, часть 3

Сегодня — заключительная часть рассказа о грамматике для разбора даты. В прошлый раз мы добавили класс с действиями, теперь ради упражнения добавим в этот класс атрибуты (то, что в других языках называют данными класса).

Заодно чуть усложним грамматику и научим ее парсить даты, записанные в виде 28.02.18:

token TOP {
    | <year> <sep> <month> <sep> <day>
    | <day>  <sep> <month> <sep> <year>
    | <n>    <sep> <month> <sep> <nn>
 }

. . .

token n {
    \d ** 1..2
}
token nn {
    \d ** 2
}

Класс действий теперь должен уметь различать ситуации, когда дата записана с полным годом, а когда год представлен только двумя цифрами.

Дату будем хранить в хеше %date, что немного избыточно для этой задачи, но хорошо иллюстрирует, как действовать, когда акции требуют сохранения промежуточных данных.

class DateStrActions {
    has %date;

    method TOP($/) {
        if %date<nn> {
            if %date<nn> > 35 {
                %date<year> = 1900 + %date<nn>;
            }
            else {
                %date<year> = 2000 + %date<nn>;
            }
            %date<day> = %date<n>;
        }
        printf "--> %4i-%02i-%02i\n", 
               %date<year>, %date<month>, %date<day>
    }
    method year($/) {
        %date<year> = ~$/;
    }
    method month($/) {
        %date<month> = ~$/;
    }
    method day($/) {
        %date<day> = ~$/;
    }
    method nn($/) {
        %date<nn> = ~$/;
    }
    method n($/) {
        %date<n> = ~$/;
    }
}

Здесь действия должны проявить сообразительность, чтобы правильно оценить, к какому столетию относится год.

Теперь самое важное. Поскольку в классе появился атрибут, компилятору потребуется выделить под него место. Для этого от вас требуется создать объект, а не просто передать имя класса:

my $r = DateStr.parse($t, :actions(DateStrActions.new()));

Тестируем код на разных датах:

$ perl6 actions2.pl
2018-02-26 --> 2018-02-26
2019-3-27  --> 2019-03-27
28.04.2020 --> 2020-04-28
30.05.21   --> 2021-05-30
15.06.99   --> 1999-06-15

Полный код вы этого варианта программы можете найти на гитхабе.

56. Что такое gist в Perl 6

Когда вы пробуете печатать объект, например: say $x, Perl 6 вызывает метод gist. Этот метод определен для всех встроенных типов — где-то он вызывает метод Str, где-то perl, а где-то формирует особое представление.

Рассмотрим, как можно воспользоваться этим методом, чтобы сделать свой вариант печати объекта:

class X {
    has $.value;

    method gist {
        '[' ~ $!value ~ ']'
    }
}

my $x = X.new(value => 42);

say $x; # [42]
$x.say; # [42]

При обращении к say программа печатает число в квадратных скобках: [42].

Обратите внимание, что при интерполяции в строке, заключенной в двойные кавычки, вызывается другой метод — Str:

say $x.Str; # X<140586830040512>
say "$x";   # X<140586830040512>

Если вам требуется кастомная интерполяция, переопределяйте и метод Str:

class X {
    has $.value;

    method gist {
        '[' ~ $!value ~ ']'
    }
    method Str {
        '"' ~ $!value ~ '"'
    }
}

my $x = X.new(value => 42);

say $x;     # [42]
$x.say;     # [42]

say $x.Str; # "42"
say "$x";   # "42"