2009-11-26 15 views
7

में पार्सिंग रिकर्सिव संरचनाएं मैं स्कैला में एक पार्सर का निर्माण करने की कोशिश कर रहा हूं जो सरल एसक्यूएल-जैसी तारों को पार्स कर सकता है। मैं काम कर रहा मूल बातें मिल गया है और की तरह कुछ पार्स कर सकते हैं:स्कैला

select id from users where name = "peter" and age = 30 order by lastname 

लेकिन अब मैं नेस्टेड और कक्षाओं पार्स करने के लिए कैसे सोचा, यानी

select name from users where name = "peter" and (age = 29 or age = 30) 

मेरे 'combinedPredicate' के वर्तमान उत्पादन इस तरह दिखता है :

def combinedPredicate = predicate ~ ("and"|"or") ~ predicate ^^ { 
    case l ~ "and" ~ r => And(l,r) 
    case l ~ "or" ~ r => Or(l,r) 
} 

मैंने संयुक्त रूप से संयुक्त राष्ट्र उत्पादन को संदर्भित करने की कोशिश की लेकिन इसका परिणाम स्टैक ओवरफ्लो में हुआ।

btw, मैं बस यहाँ प्रयोग कर रहा हूँ ... को लागू पूरे एएनएसआई -99 कल्पना नहीं;)

उत्तर

11

खैर, प्रत्यावर्तन किसी भी तरह सीमांकित किया जाना है। इस मामले में, आप ऐसा कर सकता है:

def combinedPredicate = predicate ~ rep(("and" | "or") ~ predicate) 
def predicate = "(" ~ combinedPredicate ~ ")" | simplePredicate 
def simplePredicate = ... 

तो यह अतिप्रवाह ढेर कभी नहीं होगा, क्योंकि recurse करने, यह पहली एक चरित्र स्वीकार करने के लिए है। यह एक महत्वपूर्ण हिस्सा है - यदि आप हमेशा सुनिश्चित करते हैं कि किसी चरित्र को स्वीकार किए बिना रिकर्सन नहीं होगा, तो आप कभी भी अनंत रिकर्सन में नहीं पहुंच पाएंगे। बेशक, आपके पास अनंत इनपुट नहीं है। ;)

0

ऑपरेटर पूर्वता के लिए समाधान के बारे में पढ़ने के बाद और

def combinedPredicate = predicate ~ ... 
def predicate = combinedPrediacate | ... 

स्काला 2.7 में पदव्याख्यायित्र combinators पुनरावर्ती डिसेंट पारसर्स हैं: का सामना कर फिर से शायद एक बाएं पुनरावर्ती भाषा का परिणाम है। रिकर्सिव वंश के पार्सर्स में इनके साथ समस्याएं हैं क्योंकि उन्हें पता नहीं है कि टर्मिनल प्रतीक कब होता है जब उन्हें पहली बार सामना करना पड़ता है। स्काला 2.8 के packrat पार्सर combinators तरह पारसर्स के अन्य प्रकार के ही आप व्याकरण तरीकों के बजाय lazy val s का उपयोग करके, इसलिए की तरह निर्धारित करने होंगे, इस के साथ कोई समस्या नहीं होगा: वैकल्पिक रूप से

lazy val combinedPredicate = predicate ~ ... 
lazy val predicate = combinedPrediacate | ... 

, आप refactor सकता है बाएं रिकर्सन से बचने के लिए भाषा। उदाहरण से आप मुझे दे रहे हैं, इस भाषा में कोष्ठक की आवश्यकता है समस्या को प्रभावी ढंग से हल कर सकता है।

def combinedPredicate = predicate ~ ... 
def predicate = "(" ~> combinedPrediacate <~ ")" | ... 

अब रिकर्सन का प्रत्येक गहरा स्तर पार्स किए गए दूसरे कोष्ठक से मेल खाता है। आप जानते हैं कि जब आप कोष्ठक से बाहर निकलते हैं तो आपको गहरी देखभाल करने की आवश्यकता नहीं होती है।

7

ढेर अतिप्रवाह आप '

def clause:Parser[Clause] = (predicate|parens) * (
      "and" ^^^ { (a:Clause, b:Clause) => And(a,b) } | 
      "or" ^^^ { (a:Clause, b:Clause) => Or(a,b) }) 

    def parens:Parser[Clause] = "(" ~> clause <~ ")" 

कौन सा शायद सिर्फ एक और तरीका लिख ​​क्या @Daniel लिखा है: निम्नलिखित के साथ आया था :-)

+1

"आलसी वाल" के संबंध में कृपया नई पैट्रेट क्षमताओं का उपयोग करने के लिए ": पार्सर [किसी भी]" से ": पैट्रैटपार्सर [किसी भी]" से स्पष्ट प्रकार की घोषणाओं को बदलने के लिए भी याद रखें। (जैसा कि आपने मेरे प्रश्न में बताया है http://stackoverflow.com/questions/3343697/scala-parser-combinators-tricks-for-recursive-bnf) – svrist