$^R

2017-11-05 38 views
6

के साथ perl regexp का उपयोग करके नेस्टेड टुपल्स को पार्सिंग करना मैं एम्बेडेड कोड निष्पादन के साथ एक पर्ल रेगेक्सपी का उपयोग करके नेस्टेड टुपल्स के लिए एक अमूर्त वाक्यविन्यास पेड़ कैसे बनाना सीखना चाहता हूं। मैं आसानी से प्रोग्राम कर सकता हूं कि एक पर्ल 6 व्याकरण का उपयोग करके और मुझे पता है कि पार्सिंग मॉड्यूल का उपयोग करके पर्ल 5 में कार्य को सरल बना दिया जाएगा, लेकिन मुझे लगता है कि इस तरह के सरल कार्यों के लिए मुझे यह सीखकर मॉड्यूल के बिना ऐसा करने में सक्षम होना चाहिए कि कैसे यांत्रिक रूप से अनुवाद करना है व्याकरण परिभाषाएं। मुझे $^R को कम करने का कोई तरीका नहीं मिला, इसलिए मैं TUPLE नियम परिभाषा के अंत में अनैच्छिक घोंसले को पूर्ववत करने का प्रयास करता हूं, लेकिन आउटपुट गलत है, उदा। कुछ सबस्ट्रिंग दो बार दिखाई देते हैं।

use v5.10; 
use Data::Dumper; 

while (<DATA>) { 
    chomp; 
    /(?&TUPLE)(?{$a = $^R}) 
    (?(DEFINE) 
     (?<TUPLE> 
      T \s (?&ELEM) \s (?&ELEM) 
      (?{ [$^R->[0][0],[$^R->[0][1],$^R[1]]] }) 
     ) 
     (?<ELEM> 
      (?: (a) (?{ [$^R,$^N] }) | \((?&TUPLE) \)) 
     ) 
    )/x; 
    say Dumper $a; 
} 

__DATA__ 
T a a 
T (T a a) a 
T a (T a a) 
T (T a a) (T a a) 
T (T (T a a) a) (T a (T a a)) 

अपेक्षित उत्पादन डेटा संरचना एक नेस्टेड सूची है:

['a','a']; 
['a',['a','a']]; 
[['a','a'],'a']; 
[['a','a'],['a','a']]; 
[[['a','a'],'a'],['a',['a','a']]] 

मैं अपने पर्ल 6 कोड काम साझा करेंगे संदर्भ के लिए:

grammar Tuple { 
    token TOP { 'T ' <elem> ' ' <elem> } 
    token elem { 'a' | '(' <TOP> ')'} 
} 
class Actions { 
    method TOP($/) {make ($<elem>[0].made, $<elem>[1].made)} 
    method elem($/) {make $<TOP> ?? $<TOP>.made !! 'a'} 
} 

उत्तर

6

कैसे उपयोग करने के लिए यह पता लगाने की कोशिश कर रहा (?{ ... }) संरचनाएं हमेशा प्रयास के लायक नहीं हैं। विशेष रूप से, यह बैकट्रैकिंग के साथ अप्रत्याशित व्यवहार हो सकता है। इस तरह के regexes डीबग करना भी बहुत मुश्किल है क्योंकि नियंत्रण प्रवाह गैर स्पष्ट है।

इसके बजाय, m//gc -स्टाइल लेक्सिंग के साथ एक विज्ञापन-आवर्ती रिकर्सिव वंश पार्सर लिखना आसान होता है: प्रत्येक पर्ल स्ट्रिंग अपने अंतिम मैच ऑफसेट को संग्रहीत करती है। स्केलर संदर्भ में m/\G ... /gc के साथ रेगेक्स लागू करते समय, यह अंतिम ऑफ़सेट पर एंकर कर सकता है और अगर मैच सफल होता है तो ऑफसेट को ऑफ़सेट करता है।

यहाँ:

use strict; 
use warnings; 
use Test::More; 

sub parse { 
    my ($str) = @_; 
    pos($str) = 0; # set match position to beginning 
    return parse_tuple(\$str); 
} 

sub parse_tuple { 
    my ($ref) = @_; 
    $$ref =~ /\G T \s/gcx or die error($ref, "expected tuple start T"); 
    my $car = parse_element($ref); 
    $$ref =~ /\G \s /gcx or die error($ref, "expected space between tuple elements"); 
    my $cdr = parse_element($ref); 
    return [$car, $cdr]; 
} 

sub parse_element { 
    my ($ref) = @_; 
    return 'a' if $$ref =~ /\G a /gcx; 

    $$ref =~ /\G \(/gcx or die error($ref, "expected opening paren for nested tuple"); 
    my $tuple = parse_tuple($ref); 
    $$ref =~ /\G \) /gcx or die error($ref, "expected closing paren after nested tuple"); 
    return $tuple; 
} 

sub error { 
    my ($ref, $msg) = @_; 
    my $snippet = substr $$ref, pos($$ref), 20; 
    return "$msg just before '$snippet...'"; 
} 

is_deeply parse('T a a'), ['a','a']; 
is_deeply parse('T (T a a) a'), [['a','a'],'a']; 
is_deeply parse('T a (T a a)'), ['a',['a','a']]; 
is_deeply parse('T (T a a) (T a a)'), [['a','a'],['a','a']]; 
is_deeply parse('T (T (T a a) a) (T a (T a a))'), [[['a','a'],'a'],['a',['a','a']]]; 
done_testing; 
+0

कूल डिबग मुश्किल है ;-),। आप parse_tuple में संदर्भ के रूप में स्ट्रिंग पैरामीटर क्यों पास करते हैं? – rubystallion

+2

@ रूबस्टेलियन क्योंकि वर्तमान 'pos' जहां '\ G' एंकर स्ट्रिंग मान का हिस्सा हैं, हमें एक प्रतिलिपि नहीं बनाना चाहिए (या हमें प्रत्येक उप में फिर से' pos' असाइन करना होगा)। ध्यान दें कि 'parse_element()' उस मिलान को जारी रख सकता है जहां 'parse_tuple() 'समाप्त हो गया था क्योंकि' $$ ref' में सही pos है। बड़ी दस्तावेजों के लिए प्रतियां भी अक्षम होती हैं। – amon

+1

मुझे लगता है कि यह उन स्थानों में से एक है जहां पैरामीटर के बजाय '$ _' का उपयोग करना समझदारी होगी। – ikegami

0

मैं अपने प्रश्न में कोड तय की। बाहर निकलता है मैंने गलती से $^R[1]$^R->[1] के बजाय लिखा है। तो अब मुझे समझ में क्यों आमोन ने कहा कि इन निर्माणों इसे लिखने यह यह आसान बनाता है की तरह

use v5.10; 
use Data::Dumper; 

while (<DATA>) { 
    chomp; 
    /(?&TUPLE)(?{$a = $^R->[1]}) 
    (?(DEFINE) 
     (?<TUPLE> 
      T \s (?&ELEM) \s (?&ELEM) 
      (?{ [$^R->[0][0],[$^R->[0][1],$^R->[1]]] }) 
     ) 
     (?<ELEM> 
      (?: (a) (?{ [$^R,$^N] }) | \((?&TUPLE) \)) 
     ) 
    )/x; 
    say Dumper $a; 
} 

__DATA__ 
T a a 
T (T a a) a 
T a (T a a) 
T (T a a) (T a a) 
T (T (T a a) a) (T a (T a a))