Raku : 캡처 마커의 효과가 "높은 수준"에서 손실 됨
다음 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
.
이 문제를 해결할 방법이 있습니까?
답변
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 % 기능으로 "할 수 있습니다"라고 말할 것입니다. , 그리고 다른 사람들의 생각에 따라.
<(
및 )>
캡처 마커는 주어진 토큰을 주어진 내에서 작동합니다. 기본적으로 각 토큰 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
당신이 가지고 있는 토큰 을 볼 때 불연속적인 일치를 만들 수 없다는 것을 의미합니다. 그래서 그것은 .from
6으로, .to
21로 설정되었습니다 .
이를 해결하는 방법에는 (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입니다.
STR 및 토큰 값 : 오히려 자신의 토큰 값 압연보다 NUM을 당신은 민 (+) 및 STR (~) 일치를위한 정규식 부울 수표를 사용할 수 있습니다 - 나에게 설명한 바와 같이 여기 문서화 여기
token number { \S+ <?{ defined +"$/" }> } token string { \S+ <?{ defined ~"$/" }> }