Если регулярные выражения являются методами, то какому классу они соответствуют? - PullRequest
5 голосов
/ 27 июня 2019

Регулярные выражения на самом деле являются методами:

say rx/foo/.^mro # ((Regex) (Method) (Routine) (Block) (Code) (Any) (Mu))

В этом случае это означает, что они могут действовать на себя и являются частью класса.Каким будет этот класс?Я догадываюсь, что это класс Match и что они на самом деле действуют на $ / (что на самом деле и есть).Любой другой способ сформулировать это?

Ответы [ 3 ]

8 голосов
/ 28 июня 2019

В конечном итоге все регулярные выражения ожидают получить инвокант типа Match или некоторый подкласс Match.В Perl 6 инвокант является просто первым аргументом и не является каким-либо другим особенным.

Те регулярные выражения, объявленные с rule, token или regex в пакете, будут установлены как методына этом пакете.Чаще всего они объявляются в grammar, который является ничем иным, как class, родительским элементом по умолчанию которого является Grammar, а не Any.Grammar является подтипом Match.

grammar G {}.^mro.say # ((G) (Grammar) (Match) (Capture) (Cool) (Any) (Mu))

Таким образом, вполне естественно рассматривать их как просто методы, но с телом, написанным на другом языке.Фактически, это именно то, чем они являются.

Немного сложнее увидеть, как анонимные регулярные выражения являются методами, поскольку они не устанавливаются в таблицу методов любого типа.Однако, если мы напишем:

class C {
    method foo() { 42 }
}
my $m = anon method () { self.foo }
say C.$m()

Тогда мы увидим, что можем разрешать символы в инвоканте через self, даже если этот метод фактически не установлен в классе C.То же самое с анонимными регулярными выражениями.Причина, по которой это имеет значение, заключается в том, что утверждения типа <ident>, <.ws>, <?before foo> и друзья на самом деле компилируются в вызовы методов.

Таким образом, анонимные регулярные выражения являются методами и, таким образом, обрабатывают свой первый аргумент как инвокант, это то, что позволяет разрешать различные встроенные правила, объявленные в Match.

7 голосов
/ 27 июня 2019

Метод не должен соответствовать ни одному классу:

my method bar () { say self, '!' }

bar 'Hello World'; # Hello World!


my regex baz { :ignorecase 'hello world' }

'Hello World' ~~ /<baz>/;
'Hello World' ~~ &baz;
&baz.ACCEPTS('Hello World'); # same as previous line

# baz 'Hello World';

По умолчанию методы и регулярные выражения расширений имеют отношение has с любым классом, в котором они объявлены.

class Foo {
        method bar () { say self, '!' }
  # has method bar () { say self, '!' }

        regex  baz    { :ignorecase 'hello world' }
  # has regex  baz () { :ignorecase 'hello world' }
}

Регулярному выражению нужны некоторые требования, какими бы ни были его инвоканты.

Просто запустив его как подпрограмму, он сообщает вам первое:

my regex baz { :ignorecase 'hello world' }

baz 'Hello World';
No such method '!cursor_start' for invocant of type 'Str'
  in regex baz at <unknown file> line 1
  in block <unit> at <unknown file> line 1

Обычно регулярное выражение объявляется внутри класса, объявленного с grammar.

grammar Foo {
}

say Foo.^mro;
# ((Foo) (Grammar) (Match) (Capture) (Cool) (Any) (Mu))

Таким образом, требования, вероятно, выполняются Grammar, Match или Captureв этом случае.

Это также может быть из роли, с которой он состоит.

say Foo.^roles.map(*.^name);
# (NQPMatchRole)

Есть еще больше оснований полагать, что это Match или Capture

my regex baz {
    ^
    { say 'baz was called on: ', self.^name }
}
&baz.ACCEPTS(''); # baz was called on: Match
my regex baz ( $s ) {
    :ignorecase
    "$s"
}
baz Match.new(orig => 'Hello World'), 'hello';
# 「Hello」

Я не вижу причин, по которым кто-то не мог бы сделать это самостоятельно в обычном классе.


Обратите внимание, что $/ - это просто переменная.То, что сказать, что оно передано в регулярное выражение, является неправильным пониманием ситуации.

my regex baz ( $/ ) {
    :ignorecase
    "$/"
}
'Hello World' ~~ /<baz('hello')>/;
# 「Hello」
#  baz => 「Hello」

Было бы точнее сказать, что при вызове регулярного выражения изнутри другого текущий $/ используется каквызов метода / регулярного выражения.
(я не совсем уверен, что это именно то, что происходит.)

Так что предыдущий пример будет выглядеть примерно так:

'Hello World' ~~ /{ $/.&baz('hello') }/;
3 голосов
/ 29 июня 2019

Это объяснение объединяет то, что, как мне кажется, Брэд ++ и Джонатан ++ только что научили меня, с тем, что, как я думал, я уже знал, с тем, что я обнаружил, когда копал дальше.

(Моя первоначальная цель состояла в том, чтобы напрямую объяснить загадочный Брэд No such method '!cursor_start' сообщение. Я пока что потерпел неудачу и вместо этого только что подал отчет об ошибке , но вот что еще я закончил.)

Методы

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

method bar {} # Useless declaration of a has-scoped method in mainline

Но на самом деле методы также работают нормально, так как:

Что действительно делаетМетоды Методы в том, что они являются подпрограммами с «invocant» .Инвокант - это первый параметр специального состояния, который:

  • неявно вставляется в сигнатуру метода, если он не объявлен явно.Если метод объявлен внутри класса, то этим типом является ограничение типа, в противном случае это Mu:
        class foo { my method bar {} .signature .say } # (foo: *%_)
                    my method bar {} .signature .say   # (Mu: *%_)
  • Требуется позиционирование.Таким образом:
        my method bar {}
        bar # Too few positionals passed; expected 1 argument but got 0
  • всегда имеет псевдоним self.Таким образом:
        my method bar { say self }
        bar 42 # 42
  • иногда явно объявляется путем указания его в качестве первого параметра в подписи и последующего двоеточия (:).Таким образом:
        my method bar (Int \baz:) { say baz } 
        say &bar.signature; # (Int \baz: *%_)
        bar 42;             # 42
        bar 'string';       # Type check failed in binding to parameter 'baz'

Регулярные выражения

Сосредоточив внимание только на инвокантной перспективе, регулярные выражения являются методами, которые принимают / ожидают совпадающий объект в качестве своего инвоканта.

Регулярное выражениеобычно вызывается в трех несколько разных сценариях:

  • Прямым использованием. Например my regex foo { . }; say 'a' ~~ &foo; # 「a」 (или просто say 'a' ~~ / . /; # 「a」, но я расскажу только опо сути идентичный названный пример для упрощения моего объяснения).Это означает say &foo.ACCEPTS: 'a'.Это, в свою очередь, реализовано этим кодом в Rakudo .Как видите, это вызывает регулярное выражение foo с инвокантом Match.'!cursor_init'(...) - , который запускает этот код без :build.В результате foo получает новый объект Match в качестве инвоканта.

  • посредством .parse метода класса Grammar. Метод .parse создает новый экземпляр грамматики и затем вызывает верхнее "правило" (rule / token / regex / method) для этого нового объекта грамматики.Обратите внимание, что Grammar является подклассом Match;поэтому, как и в первом сценарии, правилу / регулярному выражению передается пока еще пустой объект соответствия.Если верхнее правило соответствует, новый объект грамматики / соответствия будет возвращен вызовом .parse.(В противном случае он вернет Nil.)

  • В порядке одного из вышеперечисленных. Верхнее правило в грамматике обычно содержит вызовы нижнего уровня.правила / маркеры / регулярные выражения / методы.Аналогично, автономное правило / регулярное выражение может содержать обращения к другим правилам / регулярным выражениям.Каждый такой вызов будет включать создание другого нового объекта грамматики / соответствия, который становится инвокантом для вложенного вызова.Если вложенный вызов совпадает, и это вызов захвата, то новый объект грамматики / сопоставления добавляется в объект грамматики / сопоставления более высокого уровня.

...