2009-11-14 9 views
5

मैं pyparsing का उपयोग कर बैकस्लैश-न्यूलाइन संयोजन ("\\n") के साथ कई पंक्तियों पर टूटने वाले शब्दों को पार्स करने का प्रयास कर रहा हूं। यहाँ मैं क्या किया है है:एकाधिक पंक्तियों पर एक शब्द से बचने के लिए पाइपर्सिंग का उपयोग करना

from pyparsing import * 

continued_ending = Literal('\\') + lineEnd 
word = Word(alphas) 
split_word = word + Suppress(continued_ending) 
multi_line_word = Forward() 
multi_line_word << (word | (split_word + multi_line_word)) 

print multi_line_word.parseString(
'''super\\ 
cali\\ 
fragi\\ 
listic''') 

उत्पादन मैं ['super'] है, जबकि उम्मीद उत्पादन ['super', 'cali', fragi', 'listic'] है। अभी भी बेहतर है उन सभी को एक शब्द के रूप में शामिल हो गए हैं (जो मुझे लगता है कि मैं सिर्फ multi_line_word.parseAction(lambda t: ''.join(t)) साथ क्या कर सकते हो जाएगा।

मैं pyparsing helper में इस कोड को देख कोशिश की, लेकिन यह मुझे एक त्रुटि देता है, maximum recursion depth exceeded

संपादित करें 2009-11-15: मुझे बाद में एहसास हुआ कि सफेद अंतरिक्ष के संबंध में पाइपर्सिंग थोड़ा उदार हो जाता है, और इससे कुछ खराब धारणाएं होती हैं जो मैंने सोचा था कि मैं पार्सिंग कर रहा था, बहुत कम था। यही कहना है, हम चाहते हैं शब्द, भागने, और ईओएल चरित्र के किसी भी भाग के बीच कोई सफेद स्थान नहीं देखें।

मुझे एहसास हुआ कि उपरोक्त छोटी उदाहरण स्ट्रिंग एक परीक्षण मामले के रूप में अपर्याप्त है, इसलिए मैंने निम्नलिखित यूनिट परीक्षण लिखे हैं। कोड जो इन परीक्षणों को पास करता है, वह मुझे बचने में सक्षम होना चाहिए जो मैं सहजता से बचने के लिए — और केवल एक भागने-विभाजित शब्द के रूप में सोचने में सक्षम होना चाहिए। वे एक ऐसे मूल शब्द से मेल नहीं खाएंगे जो बचने वाला नहीं है। हम — कर सकते हैं और मेरा मानना ​​है कि — उस के लिए एक अलग व्याकरणिक निर्माण का उपयोग करना चाहिए। यह दो अलग होने के साथ यह सब साफ रखता है।

import unittest 
import pyparsing 

# Assumes you named your module 'multiline.py' 
import multiline 

class MultiLineTests(unittest.TestCase): 

    def test_continued_ending(self): 

     case = '\\\n' 
     expected = ['\\', '\n'] 
     result = multiline.continued_ending.parseString(case).asList() 
     self.assertEqual(result, expected) 


    def test_continued_ending_space_between_parse_error(self): 

     case = '\\ \n' 
     self.assertRaises(
      pyparsing.ParseException, 
      multiline.continued_ending.parseString, 
      case 
     ) 


    def test_split_word(self): 

     cases = ('shiny\\', 'shiny\\\n', ' shiny\\') 
     expected = ['shiny'] 
     for case in cases: 
      result = multiline.split_word.parseString(case).asList() 
      self.assertEqual(result, expected) 


    def test_split_word_no_escape_parse_error(self): 

     case = 'shiny' 
     self.assertRaises(
      pyparsing.ParseException, 
      multiline.split_word.parseString, 
      case 
     ) 


    def test_split_word_space_parse_error(self): 

     cases = ('shiny \\', 'shiny\r\\', 'shiny\t\\', 'shiny\\ ') 
     for case in cases: 
      self.assertRaises(
       pyparsing.ParseException, 
       multiline.split_word.parseString, 
       case 
      ) 


    def test_multi_line_word(self): 

     cases = (
       'shiny\\', 
       'shi\\\nny', 
       'sh\\\ni\\\nny\\\n', 
       ' shi\\\nny\\', 
       'shi\\\nny ' 
       'shi\\\nny captain' 
     ) 
     expected = ['shiny'] 
     for case in cases: 
      result = multiline.multi_line_word.parseString(case).asList() 
      self.assertEqual(result, expected) 


    def test_multi_line_word_spaces_parse_error(self): 

     cases = (
       'shi \\\nny', 
       'shi\\ \nny', 
       'sh\\\n iny', 
       'shi\\\n\tny', 
     ) 
     for case in cases: 
      self.assertRaises(
       pyparsing.ParseException, 
       multiline.multi_line_word.parseString, 
       case 
      ) 


if __name__ == '__main__': 
    unittest.main() 

उत्तर

5

थोड़ा अधिक के लिए चारों ओर poking के बाद, मैं this help thread पर आया था, जहां इस उल्लेखनीय बिट

मैं अक्सर अक्षम व्याकरण देखते हैं जब किसी एक BNF परिभाषा से एक pyparsing व्याकरण सीधे लागू करता था। BNF "एक या अधिक" की अवधारणा नहीं है या "शून्य या अधिक" या "वैकल्पिक" ...

इसी के साथ

, मैं विचार इन दो पंक्तियों को बदलने के लिए मिल गया

multi_line_word = Forward() 
multi_line_word << (word | (split_word + multi_line_word)) 

करने के लिए
multi_line_word = ZeroOrMore(split_word) + word 

इस उत्पादन के लिए मिल गया मैं क्या देख रहा था: ['super', 'cali', fragi', 'listic']

multi_line_word.setParseAction(lambda t: ''.join(t)) 

यह ['supercalifragilistic'] के अंतिम आउटपुट देता है:

इसके बाद, मैं एक पार्स कार्रवाई है कि इन टोकन एक साथ शामिल हो जाएगा गयी।

घर ले जाने वाला घर संदेश यह है कि कोई भी walk into Mordor नहीं है।

बस मजाक कर रहे हैं।

घर लेना संदेश यह है कि कोई भी बीईएफ के एक-से-एक अनुवाद को पाइपर्सिंग के साथ लागू नहीं कर सकता है। पुनरावृत्त प्रकारों का उपयोग करने वाली कुछ चालों को उपयोग में बुलाया जाना चाहिए।

संपादित 2009-11-25:

no_space = NotAny(White(' \t\r')) 
# make sure that the EOL immediately follows the escape backslash 
continued_ending = Literal('\\') + no_space + lineEnd 
word = Word(alphas) 
# make sure that the escape backslash immediately follows the word 
split_word = word + NotAny(White()) + Suppress(continued_ending) 
multi_line_word = OneOrMore(split_word + NotAny(White())) + Optional(word) 
multi_line_word.setParseAction(lambda t: ''.join(t)) 

यह सुनिश्चित करें कि कोई अंतरिक्ष किसी भी के बीच आता है बनाने को लाभ मिलता है: अधिक ज़ोरदार परीक्षण मामलों के लिए क्षतिपूर्ति करने के लिए, मैं निम्नलिखित करने के लिए कोड को संशोधित किया तत्वों के (बैकस्लाश से बचने के बाद न्यूलाइन के अपवाद के साथ)।

+1

'संयोजन' का उपयोग करने से कोई हस्तक्षेप करने वाली व्हाइटस्पेस भी लागू नहीं होती है। – PaulMcG

+0

दिलचस्प। कोशिश की 'multi_line_word = संयोजन (संयोजन (OneOrMore (split_word)) + वैकल्पिक (शब्द))' लेकिन यह 'sh \\\ n iny' मामले पर टूट जाता है जिसमें यह अपवाद नहीं उठाता है, बल्कि इसके बजाय वापस '[' sh '] '। क्या मैं कुछ भूल रहा हूँ? – gotgenes

+0

ठीक है, आपका शब्द सिर्फ '\' - न्यूलाइन में फैले अक्षरों में नहीं है, लेकिन वहां 'i' अक्षर से पहले उस स्थान पर है, जो शब्द ब्रेक के रूप में गिना जाता है, इसलिए 'sh' के बाद संयोजन बंद हो जाता है। आप * एक आसन्न = झूठी कन्स्ट्रक्टर तर्क के साथ संयोजन को संशोधित कर सकते हैं, लेकिन सावधान रहें - आप पूरी फ़ाइल को एक शब्द के रूप में चूसने लग सकते हैं! या यदि आप किसी भी अग्रणी रिक्त स्थान को भी पतन करना चाहते हैं, तो आप लाइन के बाद किसी भी व्हाइटस्पेस को शामिल करने के लिए निरंतर_करण की अपनी परिभाषा को फिर से परिभाषित कर सकते हैं। – PaulMcG

5

आप अपने कोड के बहुत करीब हैं। इन mods के किसी भी काम करेगा:

# '|' means MatchFirst, so you had a left-recursive expression 
# reversing the order of the alternatives makes this work 
multi_line_word << ((split_word + multi_line_word) | word) 

# '^' means Or/MatchLongest, but beware using this inside a Forward 
multi_line_word << (word^(split_word + multi_line_word)) 

# an unusual use of delimitedList, but it works 
multi_line_word = delimitedList(word, continued_ending) 

# in place of your parse action, you can wrap in a Combine 
multi_line_word = Combine(delimitedList(word, continued_ending)) 

आप अपने pyparsing Googling में पाया के रूप में, BNF-> pyparsing अनुवाद BNF, उम, कमियों के स्थान पर pyparsing सुविधाओं का उपयोग करने के लिए एक विशेष दृश्य के साथ किया जाना चाहिए। मैं वास्तव में लंबे उत्तर लिखने के बीच में था, बीएनएफ अनुवाद मुद्दों में से अधिक में जा रहा था, लेकिन आप पहले से ही इस सामग्री को मिला है (विकी पर, मुझे लगता है)।

संबंधित मुद्दे