2011-07-25 22 views
21

मैं स्काला के काफी नई हूँ और पार्सर combinators (The Magic Behind Parser Combinators, Domain-Specific Languages in Scala) के बारे में पढ़ते समय मैं इस तरह विधि परिभाषाओं में आए:स्काला के पार्सर में टिल्ड समझना combinators

def classPrefix = "class" ~ ID ~ "(" ~ formals ~ ")" 

मैं सोचा पढ़ने किया गया है Scala.util.parsing का एपीआई दस्तावेज़। पार्सर्स जो नाम (tilde) नामक विधि को परिभाषित करता है लेकिन मैं अभी भी ऊपर दिए गए उदाहरण में इसका उपयोग नहीं समझता हूं। उस उदाहरण में (tilde) एक विधि है जिसे java.lang.String पर कहा जाता है जिसमें उस विधि नहीं होती है और संकलक विफल होने का कारण बनता है। मुझे पता है कि (टिल्ड)

case class ~ [+a, +b] (_1: a, _2: b) 

लेकिन के रूप में परिभाषित किया जाता है कैसे करता है ऊपर के उदाहरण में इस मदद?

मुझे खुशी होगी अगर कोई मुझे यह समझने में संकेत दे कि क्या हो रहा है। अग्रिम में बहुत बहुत धन्यवाद!

जनवरी

उत्तर

30

यहां संरचना थोड़ा मुश्किल है। सबसे पहले, ध्यान दें कि आप हमेशा इन चीजों को के अंदर कुछ पार्सर के उप-वर्ग को परिभाषित करते हैं, उदा। class MyParser extends RegexParsers। अब, आप RegexParsers अंदर दो अंतर्निहित परिभाषाओं नोट कर सकते हैं:

implicit def literal (s: String): Parser[String] 
implicit def regex (r: Regex): Parser[String] 

क्या इन करना होगा किसी भी स्ट्रिंग या रेगुलर एक्सप्रेशन से लेते हैं और उन्हें एक पार्सर कि कि स्ट्रिंग या निशानी के रूप में है कि regex से मेल खाता में तब्दील है। वे निहित हैं, इसलिए उन्हें किसी भी समय लागू होने पर लागू किया जाएगा (उदाहरण के लिए यदि आप Parser[String] पर String (या Regex) पर कोई विधि कॉल नहीं करते हैं)।

लेकिन यह Parser चीज़ क्या है?यह RegexParser के लिए एक आंतरिक वर्ग Parsers अंदर परिभाषित, supertrait है:

class Parser [+T] extends (Input) ⇒ ParseResult[T] 

लग रहा है कि यह एक समारोह है कि इनपुट लेता है और एक परिणाम के लिए इसे नक्शे है जैसे। खैर, यह समझ में आता है! और आप इसके लिए प्रलेखन here देख सकते हैं।

अब हम सिर्फ ~ विधि देख सकते हैं:

def ~ [U] (q: ⇒ Parser[U]): Parser[~[T, U]] 
    A parser combinator for sequential composition 
    p ~ q' succeeds if p' succeeds and q' succeeds on the input left over by p'. 

तो, अगर हम देखते हैं की तरह

def seaFacts = "fish" ~ "swim" 

कुछ क्या होता है, है पहले "fish"~ विधि नहीं है, इसलिए यह स्पष्ट रूप से Parser[String] में परिवर्तित हो गया है जो करता है। ~ विधि फिर Parser[U] प्रकार का तर्क चाहता है, और इसलिए हम "swim" को Parser[String] (यानी U == String) में निहित रूप से परिवर्तित करते हैं। अब हमारे पास कुछ ऐसा है जो इनपुट "fish" से मेल खाता है, और इनपुट में जो कुछ भी बचा है, उसे "swim" से मेल खाना चाहिए, और यदि दोनों मामले हैं, तो seaFacts इसके मिलान में सफल होगा।

+1

आपकी व्याख्या के लिए बहुत बहुत धन्यवाद। मैं स्कैला की "निहित रूपांतरण" सुविधा से परिचित नहीं था। उस विषय पर एक अच्छा लेख यहां पाया जा सकता है: http://scalada.blogspot.com/2008/03/implicit-conversions-magical-and.html – Jano

3

आप Parsers.Parser चेकआउट चाहिए। स्काला कभी-कभी पैटर्न मिलान आदि की सहायता के लिए विधि और केस क्लास को उसी नाम से परिभाषित करता है, और यदि आप स्कालाडोक पढ़ रहे हैं तो यह थोड़ा उलझन में है।

विशेष रूप से, "class" ~ ID"class".~(ID) जैसा ही है। ~ एक ऐसी विधि है जो पार्सर को अनुक्रमिक रूप से किसी अन्य पार्सर से जोड़ती है।

an implicit conversionRegexParsers में परिभाषित किया गया है जो स्वचालित रूप से String मान से एक पार्सर बनाता है। तो, "class" स्वचालित रूप से Parser[String] का उदाहरण बन जाता है।

val ID = """[a-zA-Z]([a-zA-Z0-9]|_[a-zA-Z0-9])*"""r 

RegexParsers भी एक और निहित रूपांतरण है कि स्वचालित रूप से एक Regex मूल्य से पार्सर बनाता है परिभाषित करता है। तो, ID स्वचालित रूप से Parser[String] का एक उदाहरण बन जाता है।

दो पार्सर्स को जोड़कर, "class" ~ ID एक Parser[String] देता है जो शाब्दिक "वर्ग" से मेल खाता है और फिर नियमित अभिव्यक्ति ID अनुक्रमिक रूप से दिखाई देता है। | और ||| जैसी अन्य विधियां हैं। अधिक जानकारी के लिए, Programming in Scala पढ़ें।

13

~ पार्सर पर विधि दो पार्सर को जोड़ती है जो दो मूल पार्सर्स को लगातार लागू करती है और दो परिणाम देता है। यही कारण है कि बस हो सकता है (Parser[T] में)

def ~[U](q: =>Parser[U]): Parser[(T,U)]. 

आप दो से अधिक पारसर्स संयुक्त कभी नहीं करते हैं, तो यह ठीक हो जाएगा। हालांकि, अगर आप उनमें से तीन, p1, p2, p3, चेन वापसी प्रकार T1, T2, T3, तो p1 ~ p2 ~ p3, जो p1.~(p2).~(p3) का मतलब के साथ प्रकार Parser[((T1, T2), T3)] की है। और यदि आप उनमें से पांच को अपने उदाहरण के रूप में जोड़ते हैं, तो यह Parser[((((T1, T2), T3), T4), T5)] होगा। फिर जब आप परिणाम पर पैटर्न मिलान करते हैं, तो आपके पास उन सभी पैराथेन्स भी होंगे:

case ((((_, id), _), formals), _) => ... 

यह काफी असहज है।

फिर एक चालाक वाक्य रचनात्मक चाल आता है। जब किसी केस क्लास में दो पैरामीटर होते हैं, तो यह पैटर्न में उपसर्ग स्थिति के बजाय इंफिक्स में दिखाई दे सकता है। यही है, अगर आपके पास case class X(a: A, b: B) है, तो आप case X(a, b) के साथ पैटर्न मिलान कर सकते हैं, लेकिन case a X b के साथ भी। (गैर खाली सूची से मेल खाने के लिए x::xs पैटर्न के साथ ऐसा किया जाता है, :: एक केस क्लास है)। जब आप केस a ~ b ~ c लिखते हैं, तो इसका मतलब case ~(~(a,b), c) है, लेकिन case ((a,b), c) से भी अधिक सुखद और अधिक सुखद है, जो सही होने के लिए मुश्किल है।

तो ~ पार्सर में विधि Parser[(T,U)] के बजाय Parser[~[T,U]] लौटाती है, ताकि आप एकाधिक ~ के परिणाम पर आसानी से मिलान कर सकें। इसके अलावा, ~[T,U] और (T,U) उतना ही वही बात है, जैसा कि आप प्राप्त कर सकते हैं के रूप में isomorphic।

समान नाम पार्सर में संयोजन विधि के लिए चुना गया है और परिणाम प्रकार के लिए, क्योंकि परिणामी कोड पढ़ने के लिए प्राकृतिक है। एक तुरंत देखता है कि परिणाम प्रसंस्करण में प्रत्येक भाग व्याकरण नियम के सामान से कैसे संबंधित है।

parser1 ~ parser2 ~ parser3 ^^ {case part1 ~ part2 ~ part3 => ...} 

टिल्डा चुना क्योंकि इसके प्राथमिकता दी जाएगी (यह कसकर बांधता है) पार्सर पर अन्य ऑपरेटरों के साथ अच्छी तरह से निभाता है।

एक अंतिम बिंदु, सहायक ऑपरेटर ~> और <~ हैं जो ऑपरेंड में से एक के परिणाम को छोड़ देते हैं, आमतौर पर नियम में निरंतर भाग जो उपयोगी डेटा नहीं लेते हैं। तो कोई

"class" ~> ID <~ ")" ~ formals <~ ")" 

और परिणामस्वरूप केवल आईडी और सूत्रों के मान प्राप्त करेगा।

+0

क्षमा करें, मुझे स्ट्रिंग के बारे में प्रश्न का हिस्सा याद आया। @Rex Kerr ने इसे ठीक से उत्तर दिया। तो ~ पार्सर पर एक विधि है, और यह डेटा लौटाता है। और जब एक स्ट्रिंग के लिए एक विधि के रूप में लागू किया जाता है, तो यह तारों के पार्सर के अंतर्निहित रूपांतरण को ट्रिगर करता है। –