Raku : 캡처 마커의 효과가 "높은 수준"에서 손실 됨

Aug 15 2020

다음 Raku 스크립트 :

#!/usr/bin/env raku
use v6.d;

grammar MyGrammar
{
    rule TOP { <keyword> '=' <value> }
    token keyword { \w+ }
    token value { <strvalue> | <numvalue> }
    token strvalue { '"' <( <-["]>* )> '"' }
    token numvalue { '-'? \d+ [ '.' \d* ]? }
}

say MyGrammar.parse('foo = 42');
say MyGrammar.parse('bar = "Hello, World!"');

다음과 같은 출력이 있습니다.

「foo = 42」
 keyword => 「foo」
 value => 「42」
  numvalue => 「42」
「bar = "Hello, World!"」
 keyword => 「bar」
 value => 「"Hello, World!"」
  strvalue => 「Hello, World!」

두 번째 항목, 참고 strvalue캡처 시장에 의도 한대로, 따옴표없이 문자열 값을 포함하고 <(... )>. 그러나 놀랍게도, 따옴표가 있습니다 에 포함 value.

이 문제를 해결할 방법이 있습니까?

답변

6 raiph Aug 15 2020 at 22:30

TL; DR "다중 디스패치"를 사용합니다. [1,2] 상황이있는 이유에 대한 자세한 설명은 @ user0721090601의 답변을 참조하십시오. 숫자 구문이 Raku와 일치하도록하려면 @ p6steve 's에서 문법을 정말 현명하게 변경하십시오.

다중 디스패치 솔루션

이 문제를 해결할 방법이 있습니까?

한 가지 방법은 명시 적 다중 디스패치로 전환하는 것입니다.

현재 value특별히 명명 된 값 변형을 호출 하는 토큰이 있습니다.

    token value { <strvalue> | <numvalue> }

다음으로 교체하십시오.

    proto token value {*}

그런 다음 문법 다중 디스패치 타겟팅 규칙에 따라 호출 된 토큰의 이름을 바꾸면 문법은 다음과 같습니다.

grammar MyGrammar
{
    rule TOP { <keyword> '=' <value> }
    token keyword { \w+ }
    proto token value {*}
    token value:str { '"' <( <-["]>* )> '"' }
    token value:num { '-'? \d+ [ '.' \d* ]? }
}

say MyGrammar.parse('foo = 42');
say MyGrammar.parse('bar = "Hello, World!"');

다음이 표시됩니다.

「foo = 42」
 keyword => 「foo」
 value => 「42」
「bar = "Hello, World!"」
 keyword => 「bar」
 value => 「Hello, World!」

이것은 기본적으로 개별 교체를 캡처하지 않습니다. "다중 디스패치"를 고수 할 수 있지만 하위 캡처의 이름 지정을 다시 도입합니다.

grammar MyGrammar
{
    rule TOP { <keyword> '=' <value> }
    token keyword { \w+ }
    proto token value { * }
    token value:str { '"' <( $<strvalue>=(<-["]>*) )> '"' } token value:num { $<numvalue>=('-'? \d+ [ '.' \d* ]?) }
}

say MyGrammar.parse('foo = 42');
say MyGrammar.parse('bar = "Hello, World!"');

표시 :

「foo = 42」
 keyword => 「foo」
 value => 「42」
  numvalue => 「42」
「bar = "Hello, World!"」
 keyword => 「bar」
 value => 「Hello, World!」
  strvalue => 「Hello, World!」

놀라움

놀랍게도 따옴표는 value.

저도 처음에는 놀랐습니다. [삼]

그러나 현재 행동은 적어도 다음과 같은 의미에서 나에게도 의미가 있습니다.

  • 기존의 행동은 어떤 상황에서는 장점이 있습니다.

  • 내가 그것을 기대하고 있었다면 그것은 놀라운 일이 아닐 것입니다. 다른 상황에서는 그렇게했을 것입니다.

  • 그것은 경우 하나는 현재 동작을 얻을 것입니다 방식을 볼 쉬운 일이 아닙니다 되었다 싶었지만 대신 (내가) 처음에 예상대로 일했다;

  • 위에서 다룬 해결책이 있습니다.

각주

[1] 복수의 디스패치 사용 [2] 이다용액하지만 IMO 원래 문제 소정 지나치게 복잡한 것 같다. 아마도 더 간단한 해결책이있을 것입니다. 아마도 누군가가 귀하의 질문에 대한 다른 답변으로 그것을 제공 할 것입니다. 그렇지 않다면 언젠가는 훨씬 더 간단한 해결책이 하나 이상 있기를 바랍니다. 그러나 우리가 수년 동안 하나를 얻지 못하더라도 나는 놀라지 않을 것입니다. 우리는 위의 해결책을 가지고 있으며 할 일이 많이 있습니다.

[2] 메소드를선언하고, 말하고,작성할 있지만method value:foo { ... }(각 메소드가 일치 객체를 반환하는 경우), Rakudo는 일반적인 다중 메소드 디스패치 메커니즘을 사용하여 비 메소드 규칙 교체에 디스패치하지 않고 대신 NFA .

[3] 어떤 사람들은 Raku가 우리가 예상 한대로했다면 그것이 "해야한다", "할 수있다", "최고가 될 것"이라고 주장 할 수도 있습니다. 나는 다른 사람들이 고려 올릴 것을 일체의 단점을 기꺼이하지 않는 버그에 대한 oulding / 기능 [| | C 승 쉬] 나는 일반적으로 피하면 난 내 최선 생각을 발견 하고 작업을 얻을 필요합니까 도움을 기꺼이 한 일. 그래서 저는 현재 10 % 버그, 90 % 기능으로보고 있지만 특정 시나리오에서 해당 동작을 원하는지 여부에 따라 100 % 버그 또는 100 % 기능으로 "할 수 있습니다"라고 말할 것입니다. , 그리고 다른 사람들의 생각에 따라.

6 user0721090601 Aug 15 2020 at 22:46

<()>캡처 마커는 주어진 토큰을 주어진 내에서 작동합니다. 기본적으로 각 토큰 Match은 "인덱스 X ( .from)에서 인덱스 Y ( .to) 까지 원래 문자열을 일치 시켰습니다 Match. " 라는 객체를 반환하며, 이는 객체를 문자열 화할 때 고려됩니다 . 이것이 strvalue 토큰에서 일어나는 일입니다.

my $text = 'bar = "Hello, World!"'; my $m = MyGrammar.parse: $text; my $start = $m<value><strvalue>.from; # 7 my $end   = $m<value><strvalue>.to; # 20 say $text.substr: $start, $end - $start;  # Hello, World!

시작 및 종료 값이라는 두 개의 숫자 만 있음을 알 수 있습니다. 이것은 value당신이 가지고 있는 토큰 을 볼 때 불연속적인 일치를 만들 수 없다는 것을 의미합니다. 그래서 그것은 .from6으로, .to21로 설정되었습니다 .

이를 해결하는 방법에는 (a) 작업 개체 또는 (b) 다중 토큰을 사용하는 두 가지 방법이 있습니다. 둘 다 장점이 있으며 더 큰 프로젝트에서 이것을 사용하려는 방법에 따라 둘 중 하나를 선택할 수 있습니다.

기술적으로 문법 내에서 직접 동작을 정의 할 수 있지만 별도의 클래스를 통해 수행하는 것이 훨씬 쉽습니다. 그래서 우리는 당신을 위해 할 수 있습니다.

class MyActions { 
  method TOP      ($/) { make $<keyword>.made => $<value>.made }
  method keyword  ($/) { make ~$/ }
  method value    ($/) { make ($<numvalue> // $<strvalue>).made } method numvalue ($/) { make +$/ } method strvalue ($/) { make ~$/ }
}

make값을 포함하는 토큰에 값을 전달하는 각 수준 . 그리고 둘러싸는 토큰은 .made메서드 를 통해 해당 값에 액세스 할 수 있습니다 . 이것은 순수한 문자열 값으로 작업하는 대신 어떤 식 으로든 먼저 처리하고 객체 또는 이와 유사한 것을 만들고 싶을 때 정말 좋습니다.

구문 분석하려면 다음을 수행하십시오.

my $m = MyGrammar.parse: $text, :actions(MyActions); say $m.made; # bar => Hello, World!

실제로 Pair객체입니다. TOP방법 을 수정하여 정확한 결과를 변경할 수 있습니다 .

문제를 해결할 수있는 두 번째 방법은 multi token. 문법을 개발할 때 다음과 유사한 것을 사용하는 것은 매우 일반적입니다.

token foo { <option-A> | <option-B> }

하지만 액션 클래스에서 볼 수 있듯이 실제로 어떤 것이 일치하는지 확인하고 확인해야합니다. 대신 done with으로 대체 할 수 |있는 경우 멀티 토큰을 사용할 수 있습니다.

proto token foo { * }
multi token:sym<A> { ... }
multi token:sym<B> { ... }

<foo>문법에서 사용하면 마치 기준선에 있었던 것처럼 두 가지 다중 버전 중 하나와 일치합니다 <foo>. 더 좋은 점은 액션 클래스를 사용하는 경우 유사하게 $<foo>조건문이나 기타 검사 없이도 사용할 수 있고 거기에 있음을 알 수 있다는 것입니다.

귀하의 경우에는 다음과 같습니다.

grammar MyGrammar
{
    rule TOP { <keyword> '=' <value> }
    token keyword { \w+ }
    proto token value { * }
    multi token value:sym<str> { '"' <( <-["]>* )> '"' }
    multi token value:sym<num> { '-'? \d+ [ '.' \d* ]? }
}

이제 actions 객체를 사용하지 않고도 원래 예상했던대로 액세스 할 수 있습니다.

my $text = 'bar = "Hello, World!"';
my $m = MyGrammar.parse: $text;

say $m; # 「bar = "Hello, World!"」 # keyword => 「bar」 # value => 「Hello, World!」 say $m<value>; # 「Hello, World!」

참고로 두 기술을 결합 할 수 있습니다. 다음은 다중 토큰이 주어진 경우 작업 개체를 작성하는 방법입니다.

class MyActions { 
  method TOP            ($/) { make $<keyword>.made => $<value>.made } method keyword ($/) { make ~$/ } method value:sym<str> ($/) { make ~$/ } method value:sym<num> ($/) { make +$/ }
}

처음 보면 조금 더 grokkable입니다.

2 p6steve Aug 16 2020 at 03:13

STR 및 토큰 값 : 오히려 자신의 토큰 값 압연보다 NUM을 당신은 민 (+) 및 STR (~) 일치를위한 정규식 부울 수표를 사용할 수 있습니다 - 나에게 설명한 바와 같이 여기 문서화 여기

token number { \S+ <?{ defined +"$/" }> } token string { \S+ <?{ defined ~"$/" }> }