2012-01-28 31 views
7

मैं पार्सर का उपयोग कर Conduit लिखने की कोशिश कर रहा हूं। विशेष रूप से, parseOne :: Parser T दिए गए, मैं Conduit ByteString m T बनाना चाहता हूं जो बार-बार इनपुट में पार्सर लागू करता है और परिणामों को स्ट्रीम करता है।मैं सिंक को एक सिंक में कैसे बदल सकता हूं?

attoparsec-conduit एक Sink में एक Parser चालू करने के लिए sinkParser प्रदान करता है, लेकिन कैसे मैं एक Conduit में इस Sink बदल सकते हैं?

conduitSink :: (Resource m) => Sink a m b -> Conduit a m b 

जो बार-बार Sink में डेटा फ़ीड प्रत्येक परिणाम के उत्पादन के रूप में यह हो जाता है: क्या मैं के लिए देख रहा हूँ की तरह एक समारोह है। ऐसा लगता है कि इसे मैन्युअल लूप के रूप में काफी आसानी से लिखा जा सकता है, लेकिन मैं सोच रहा हूं कि कोई बेहतर तरीका है या नहीं।

कंड्यूट लाइब्रेरी में इस प्रतीत होता है-स्पष्ट कार्य की कमी मुझे लगता है कि मैं कुछ गलत कर रहा हूं; क्या इसे पूरा करने का एक बेहतर तरीका है? पाइपलाइन के बाद के चरणों द्वारा संसाधित होने के लिए उपयोग केस कच्चे बाइट को संदेश-आधारित नेटवर्क प्रोटोकॉल के पार्स किए गए रूप में बदल रहा है। मेरे पास पहले से विपरीत दिशा है (यानी Conduit T m ByteString) blaze-builder-conduit के लिए धन्यवाद, इसलिए यह चीजों को ढांचे के सबसे प्राकृतिक तरीके की तरह लग रहा था।

उत्तर

6

आपको इसके लिए SequencedSink सिस्टम का उपयोग करने की आवश्यकता है; यह एक सिंक निर्माता के बार-बार आवेदन से एक कंड्यूट बनाने के लिए एक सिंक और एक ट्रैक राज्य का उपयोग करता है।

आपके द्वारा बनाए गए सिंक को एक मान को बढ़ाने के लिए अनुकूलित किया गया है, जो एक कंड्यूट अनुक्रम के अंत में परिणाम होगा।

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

मान लिया जाये कि, उदाहरण के लिए, अपने पार्सर पार्स करता है कि [--] या [----] आदि, और T दर्शाने डैश की संख्या पार्स Int है, तो आप के रूप में इस द्वारा प्रदर्शन पार्सर की स्थिति को ट्रैक करने की जरूरत है:

Input chunk Sink result - Data.Conduit.SequencedSinkResponse 
[--][---]  Emit Nothing [2, 3] 
[---][---  Emit (Just #func) [3] 
---------  Emit (Just #func) [] 
]    Emit Nothing [12] 
       Stop 

इस मामले में, मैं पास-ऑन राज्य के रूप में Maybe (ByteString -> Data.Attoparsec.ByteString.Result) का उपयोग करता हूं; स्थिति के आधार पर एक अलग डेटा प्रकार अधिक उपयुक्त हो सकता है।

इस स्पष्ट स्ट्रीम उपचार की आवश्यकता है ताकि नलिका की पाइपलाइन प्रकृति को बनाए रखा जा सके; पार्सर कंडिट एक "बाधा" होने के नाते, हमेशा एक पार्सर को संतुष्ट करने के लिए पर्याप्त डेटा की प्रतीक्षा करते हुए, एक प्रमुख प्रदर्शन सिंक होगा।

आवश्यक सिंक के कार्यान्वयन उपलब्ध ResourceT मोनैड इंटरफ़ेस के साथ काफी छोटा होना चाहिए।

संपादित करें: बस एक पाश में अपने सिंक लागू करने वास्तव में आसान समाधान होगा, लेकिन अपने पार्सर छोटे-छोटे स्निपेट है कि अक्सर बाइट मात्रा की सीमा पर अंत पार्स करता है अगर यह थोड़ा अलग प्रदर्शन विशेषताओं होगा।

+0

धन्यवाद, मैं इसे आज़मा दूंगा। क्या इसका मतलब यह है कि मैं एटोपर्सेक-कंड्यूट का उपयोग नहीं करूँगा?यदि हां, तो इस तकनीक का उपयोग करके एक सामान्य 'conduitParser :: (AttoparsecInput a, ResourceThrow m) => पार्सर बी बी>> अपने इंटरफेस में एक एम बी' कंडिशन जोड़ने के लिए कोई बाधा होगी, या यह एक साधारण चूक है? – ehird

+0

@ehird, मुझे विश्वास है कि यह बस एक चूक है; वर्तमान 'सिंक पार्सर' कोड से पता चलता है कि इसे आसानी से इनपुट स्ट्रीम को कई बार पार्स करने के लिए परिवर्तित किया जा सकता है, क्योंकि यह ऊपर वर्णित एक समान तकनीक का उपयोग करता है, केवल यह कि पहले पार्स के बाद इनपुट उपभोग करना बंद कर देता है। – dflemstr

+0

दरअसल, ऐसा लगता है कि पहले से ही [पुल अनुरोध] है (https://github.com/snoyberg/conduit/pull/11) 'conduitParser' को attoparsec-enumerator में जोड़ना; मैं शायद उस कार्यान्वयन का उपयोग करूंगा। रास्ते में 'अनुक्रमित सिंक' के बारे में मुझे बताने के लिए धन्यवाद; दस्तावेज पढ़ने के दौरान मैंने इसे पारित किया। – ehird

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