2010-06-20 14 views
9

मैं की तरह पेड़ भाव की तरह एक सी समारोह पार्स करने के लिए कोशिश कर रहा हूँ निम्नलिखित (का उपयोग कर Spirit Parser Framework):बूस्ट आत्मा के साथ एक व्याकरण पार्स

F(A() , B(GREAT(SOME , NOT)) , C(YES)) 

इस मैं निम्नलिखित पर तीन नियमों का उपयोग करने के लिए कोशिश कर रहा हूँ व्याकरण:

template< typename Iterator , typename ExpressionAST > 
struct InputGrammar : qi::grammar<Iterator, ExpressionAST(), space_type> { 

    InputGrammar() : InputGrammar::base_type() { 
     tag = (qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z_0-9"))[ push_back(at_c<0>(qi::_val) , qi::_1) ]; 
     command = tag [ at_c<0>(qi::_val) = at_c<0>(qi::_1) ] >> "(" >> (*instruction >> ",") 
             [ push_back(at_c<1>(qi::_val) , qi::_1) ] >> ")"; 
     instruction = (command | tag) [qi::_val = qi::_1]; 
    } 
    qi::rule< Iterator , ExpressionAST() , space_type > tag; 
    qi::rule< Iterator , ExpressionAST() , space_type > command; 
    qi::rule< Iterator , ExpressionAST() , space_type > instruction; 
}; 

सूचना है कि मेरे टैग के नियम सिर्फ भाव ('समारोह के नाम) में इस्तेमाल पहचानकर्ता पर कब्जा करने की कोशिश करता है। यह भी ध्यान दें कि टैग नियम का हस्ताक्षर std::string के बजाय ExpressionAST देता है, जैसा कि अधिकांश उदाहरणों में है। कारण मैं इसे इस तरह करना चाहता हूं वास्तव में बहुत आसान है: मुझे भिन्नताओं का उपयोग करने से नफरत है और यदि संभव हो तो मैं उनसे बचूंगा। केक को रखना और इसे भी खाने के लिए बहुत अच्छा होगा मुझे लगता है।

एक आदेश एक टैग (वर्तमान नोड का नाम, एएसटी नोड का पहला स्ट्रिंग फ़ील्ड) और कोष्ठक द्वारा संलग्न तर्कों की एक चर संख्या से शुरू होना चाहिए, और प्रत्येक तर्क एक टैग या अन्य कमांड हो सकता है ।

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

तो सवाल यह है कि मुझे नहीं पता कि उपर्युक्त कोड में क्या गलत है। आप उपरोक्त व्याकरण को कैसे परिभाषित करेंगे? एक सामान्य ब्रेक का उपयोग करें और दृष्टिकोण को देखने के लिए

struct MockExpressionNode { 
    std::string name; 
    std::vector<MockExpressionNode> operands; 

    typedef std::vector<MockExpressionNode>::iterator iterator; 
    typedef std::vector<MockExpressionNode>::const_iterator const_iterator; 

    iterator begin() { return operands.begin(); } 
    const_iterator begin() const { return operands.begin(); } 
    iterator end() { return operands.end(); } 
    const_iterator end() const { return operands.end(); } 

    bool is_leaf() const { 
     return (operands.begin() == operands.end()); 
    } 
}; 

BOOST_FUSION_ADAPT_STRUCT(
    MockExpressionNode, 
    (std::string, name) 
    (std::vector<MockExpressionNode>, operands) 
) 
+0

कुछ जो मैंने हाल ही में पाया है वह यह है कि सी और सी ++ पहचानकर्ताओं के नाम पर '$' अक्षर हो सकते हैं। ताकि ए-जेड, ए-जेड, 0-9 (पहले अक्षर को छोड़कर), _ और $ सी/सी ++ पहचानकर्ता में मान्य हैं। – Cthutu

+2

@Cthutu एमएसवीसी पहचानकर्ताओं में उच्चारण वर्णों की अनुमति देता है। इसका मतलब यह मानक अनुपालन नहीं है। –

+0

अधिक महत्वपूर्ण बात यह है कि आप जिस बिंदु को @Cthutu बनाने की कोशिश कर रहे हैं? पहचानकर्ताओं में कमी है? क्या आपका कंपाइलर नामस्थानों का सही ढंग से समर्थन नहीं करता है? – sehe

उत्तर

11

जहाँ तक डिबगिंग के रूप में, यह संभव है:

ExpressionAST प्रकार मैं उपयोग कर रहा हूँ है। यद्यपि नियमों को स्वरूपित करने के तरीके से यह मुश्किल हो गया है। यदि आप आत्मा उदाहरणों के अनुसार प्रारूपित करते हैं (~ प्रति पंक्ति एक पार्सर, प्रति पंक्ति एक फीनिक्स कथन), ब्रेक पॉइंट अधिक जानकारीपूर्ण होंगे।

आपका डेटा संरचना है कि में SOME से A() भेद करने के लिए एक तरह से वे दोनों पत्तियां हैं नहीं है (मुझे पता है कि अगर मैं कुछ याद कर रहा हूँ हैं)। आपकी भिन्नता टिप्पणी से, मुझे नहीं लगता कि यह आपका इरादा था, इसलिए इन दो मामलों में अंतर करने के लिए, मैंने bool commandFlag सदस्य चर को MockExpressionNode (A() के लिए सही और SOME के लिए गलत) के साथ जोड़ा, एक संलयन एडाप्टर लाइन के साथ।

कोड के लिए विशेष रूप से, आप, आधार निर्माता शुरू नियम पास करनी होगी यानी .:

InputGrammar() : InputGrammar::base_type(instruction) {...} 

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

tag शासन के लिए, वहाँ वास्तव में दो पारसर्स qi::char_("a-zA-Z_"), जो प्रकार char और *qi::char_("a-zA-Z_0-9") साथ _1 है जो प्रकार (मूल रूप से) vector<char> साथ _2 है।एक स्ट्रिंग में autorules बिना इन विवश इसकी संभव नहीं है, लेकिन यह प्रत्येक पार्स चार के लिए कोई नियम संलग्न द्वारा किया जा सकता है:

tag = qi::char_("a-zA-Z_") 
     [ at_c<0>(qi::_val) = qi::_1 ]; 
    >> *qi::char_("a-zA-Z_0-9")   //[] has precedence over *, so _1 is 
     [ at_c<0>(qi::_val) += qi::_1 ]; // a char rather than a vector<char> 

हालांकि, इसकी अधिक स्वच्छ भावना इस रूपांतरण करते हैं करने के लिए। तो एक नया नियम परिभाषित करें:

qi::rule< Iterator , std::string(void) , ascii::space_type > identifier; 
identifier %= qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z_0-9"); 

और इसके बारे में चिंता न करें;)। तब टैग

tag = identifier 
     [ 
      at_c<0>(qi::_val) = qi::_1, 
      ph::at_c<2>(qi::_val) = false //commandFlag 
     ] 

आदेश के लिए हो जाता है, पहले भाग ठीक है, लेकिन (*instruction >> ",")[ push_back(at_c<1>(qi::_val) , qi::_1) ] साथ एक जोड़ी समस्याओं theres। यह शून्य या एकाधिक निर्देश नियमों को "," के बाद पार्स करेगा। यह भी एक vector<MockExpressionNode> push_back को (यकीन नहीं क्यों यह या तो संकलित, शायद लापता शुरू शासन की वजह से instantiated नहीं?) प्रयास करता है। मुझे लगता है कि आप (पहचानकर्ता संशोधन के साथ) निम्नलिखित हैं:

command = 
     identifier 
     [ 
      ph::at_c<0>(qi::_val) = qi::_1, 
      ph::at_c<2>(qi::_val) = true //commandFlag 
     ] 
    >> "(" 
    >> -(instruction % ",") 
     [ 
      ph::at_c<1>(qi::_val) = qi::_1 
     ] 
    >> ")"; 

यह वैकल्पिक ऑपरेटर - और सूची ऑपरेटर % का उपयोग करता है, बाद instruction >> *("," >> instruction) के बराबर है। फीनिक्स अभिव्यक्ति तब केवल वेक्टर को सीधे संरचना सदस्य को असाइन करती है, लेकिन आप सीधे निर्देश मिलान में कार्रवाई संलग्न कर सकते हैं और push_back का उपयोग कर सकते हैं।

निर्देश नियम ठीक है, मैं बस उल्लेख करूंगा कि यह instruction %= (command|tag) के बराबर है।

एक आखिरी बात, अगर वहाँ वास्तव में A() और SOME बीच कोई फर्क नहीं है (यानी कोई commandFlag के साथ अपने मूल संरचना), तो आप इस पार्सर केवल autorules का उपयोग कर लिख सकते हैं:

template< typename Iterator , typename ExpressionAST > 
struct InputGrammar : qi::grammar<Iterator, ExpressionAST(), ascii::space_type> { 
    InputGrammar() : InputGrammar::base_type(command) { 
     identifier %= 
      qi::char_("a-zA-Z_") 
     >> *qi::char_("a-zA-Z_0-9"); 
     command %= 
      identifier 
     >> -(
      "(" 
     >> -(command % ",") 
     >> ")"); 
    } 
    qi::rule< Iterator , std::string(void) , ascii::space_type > identifier; 
    qi::rule< Iterator , ExpressionAST(void) , ascii::space_type > command; 
}; 

इस का बड़ा लाभ यह है एक संलयन लपेटा संरचना का उपयोग करना जो इनपुट को बारीकी से मॉडल करता है।

+0

हाय अकादमिक रोबोट, उत्कृष्ट पोस्ट। मैंने जवाब देने के लिए कुछ दिन लगाए क्योंकि ऑपरेटरों के बारे में पचाने के लिए बहुत कुछ था कि मैंने वास्तव में दस्तावेज़ों पर पढ़ा नहीं था। इसके अलावा क्यूई के साथ अपने commandFlag setters को बदलने के लिए कोशिश कर रहा था :: _ val.setAsFlag(); आप इसे उपयोगी पाया खुशी - लेकिन जाहिरा तौर पर _val के प्रकार ExpressionAST लेकिन किसी तरह – lurscher

+1

@lurscher के एक अभिनेता फोनिक्स आवरण के रूप में ही नहीं है। हाँ, qi :: _ वैल अभिव्यक्ति एएसटी का मूल्यांकन करेगा, लेकिन वास्तव में वह प्रकार नहीं है। सदस्य फ़ंक्शंस को कॉल करने के लिए, आप फीनिक्स बाइंड का उपयोग करेंगे (memfun 'void setAsFlag (बूल फ्लैग) के लिए '):' फीनिक्स :: बाइंड (और अभिव्यक्तिएस्ट :: सेटएएसफ्लैग, क्यूई :: _ वैल, सच)'। – academicRobot

+0

अजीब, इसमें एक निश्चित शून्य * स्वाद है .. – lurscher

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