2010-01-17 18 views
6

मैं इस साल छुट्टियों के मौसम में ऊब गया और बेतरतीब ढंग से एक सरल सूची समझ लिखने का फैसला किया/जावा के लिए पुस्तकालय छानने (मैं जानता हूँ कि वहाँ वहाँ कुछ महान लोगों को कर रहे हैं, मैं सिर्फ यह की नरक के लिए अपने स्वयं लिखना चाहते थे) ।स्ट्रिंग अभिव्यक्ति पार्सिंग युक्तियाँ?

इस सूची के लिए:

LinkedList<Person> list = new LinkedList<Person>(); 
      list.add(new Person("Jack", 20)); 
      list.add(new Person("Liz", 58)); 
      list.add(new Person("Bob", 33)); 

सिंटेक्स है:

Iterable<Person> filtered = Query.from(list).where(
    Condition.ensure("Age", Op.GreaterEqual, 21) 
    .and(Condition.ensure("Age", Op.LessEqual, 50)); 

मैं अपने बदसूरत पता है, लेकिन अगर मैं स्थिर आयात का उपयोग करें और कम विधि के नाम का उपयोग यह बहुत संक्षिप्त हो जाता है।

Iterable<Person> list2 = Query.from(list).where("x=> x.Age >= 21 & x.Age <= 50"); 

जाहिर अभिव्यक्ति पार्स मेरी नेस्टेड/एकाधिक सशर्त, पार्स करने के साथ मजबूत क्षेत्र, im होने मुसीबत नहीं है:

निम्न सिंटैक्स अंतिम लक्ष्य है। किसी को कुछ संसाधन/साहित्य के बारे में पता है मुझे मदद मिल सकती है?

मुझे केवल एक सशर्त अभिव्यक्ति मिल रही है जो इस समय स्ट्रिंग लैम्ब्डा सिंटैक्स से सफलतापूर्वक पार्स किया गया है: "x=> x.Name == Jack"। मेरी अंतर्निहित अभिव्यक्ति संरचना काफी ठोस है और आसानी से किसी भी घोंसले को संभालने में सक्षम हो सकती है, यह मुद्दा केवल एक स्ट्रिंग से पार्सिंग अभिव्यक्ति है।

धन्यवाद

बस किक के लिए है, तो यहां पर्दे के पीछे अभिव्यक्ति संरचना काम कर सकते हैं के रूप में एक छोटे से अंतर्दृष्टि (जाहिर है मैं निर्दिष्ट कर सकता है 'op.GreaterEqual', आदि ... निम्न उदाहरण में है, लेकिन मैं प्रदर्शित करने के लिए कि यह कैसे घोंसले की किसी भी राशि के लिए लचीला है) चाहता था:

Condition minAge1 = Condition.ensure("Age", Op.Equal, 20); 
Condition minAge2 = Condition.ensure("Age", Op.Greater, 20); 
Expression minAge = new Expression(minAge1, Express.Or, minAge2); 
Expression maxAge = Condition.ensure("Age", Op.Equal, 50).or(Condition.ensure("Age", Op.Less, 50)); 
Expression ageExpression = new Expression(minAge, Express.And, maxAge); 

Condition randomException = Condition.ensure("Name", Op.Equal, "Liz"); 
Expression expressionFinal = new Expression(ageExpression, Express.Or, randomException); 
+0

'x => x.Age> = 21 और x.Age <= 50" 'मेरे लिए काफी पार्स नहीं है: क्या आप इसे विस्तारित कर सकते हैं? '&' के सामने तीन अभिव्यक्तियां हैं, जो वेनिला एसक्यूएल शैली खंडों से बहुत अलग हैं। – Chii

+0

मैं अपनी उपयोगिता को एक संबंधपरक डेटाबेस तक जोड़ने के लिए प्रदाता लिखना नहीं चाहता, हालांकि यह मजेदार हो सकता है। बिल्कुल आप मुझसे क्या विस्तार करने के लिए कह रहे हैं? – jdc0589

+0

@Chii: मुझे लगता है, "x => x.Age> = 21 और x.Age <= 50" एक अज्ञात फ़ंक्शन के बराबर है जो तर्क x लेता है और अभिव्यक्ति का मूल्यांकन करके सही या गलत देता है '=>' ऑपरेटर। – MAK

उत्तर

5

असल भाव के लिए एक recursive descent parser क्या आप चाहते हैं जाएगा। यह एक विषय है जो संकलक सिद्धांत में भारी रूप से दिखाया गया है, इसलिए कंपाइलरों पर कोई भी पुस्तक विषय को कवर करेगी। औपचारिक व्याकरण संदर्भ में यह कुछ इस तरह दिखेगा:

condition : orAtom ('||' orAtom)+ ; 
orAtom  : atom ('&&' atom)+ ; 
atom  : '(' condition ')' 
      | expression ; 
expression : value OPER value ; 
value  : VARIABLE | LITERAL ' 
VARIABLE : (LETTER | '_') (LETTER | DIGIT | '_')* ; 
LITERAL : NUMBER 
      | STRING ; 
NUMBER  : '-'? DIGIT+ ('.' DIGIT+)? ; 
STRING  : '"' . CHAR* . '"' ' 
CHAR  : ('\\' | '\"' | .) + ; 
LETTER  : 'a'..'z' | 'A'..'Z' ; 
DIGIT  : '0'..'9' ; 
OPER  : '>' | '>=' | '<' | '<=' | '=' | '!=' ; 

व्याकरण ऊपर (अधिकतर) ANTLR रूप में है कि के रूप में मैं क्या साथ सबसे परिचित हूँ है।

बूलियन या अंकगणितीय भाव पार्स एक क्लासिक परिचयात्मक विषय जब पार्स के साथ काम कर ताकि आप इसे पर साहित्य के बहुत खोजने के लिए सक्षम होना चाहिए है। यदि आप एएनटीएलआर (क्योंकि आप जावा का उपयोग कर रहे हैं) का पीछा करना चाहते हैं तो मैं अत्यधिक The Definitive ANTLR Reference: Building Domain-Specific Languages पढ़ने का सुझाव दूंगा।

यदि सब इस overkill की तरह लग रहा है और सभी एक सा ज्यादा में लेने के लिए, आप सही हो सकता है। यह एक कठिन विषय में आरंभ करने के लिए है

एक वैकल्पिक आप एक मनमाना स्ट्रिंग अभिव्यक्ति बनाने के लिए लेकिन इसके बजाय एक धाराप्रवाह इंटरफ़ेस का उपयोग (आप की तरह कर रहे हैं) नहीं है:।

List results = from(source) 
    .where(var("x").greaterThan(25), var("x").lessThan(50)) 
    .select("field1", "field2"); 

कि के रूप में बताते हुए है कोड में अभिव्यक्ति वृक्ष और लागू करने के लिए आसान होना चाहिए।

+0

मुझे लगा कि वह क्षेत्र मैं देख रहा हूं। धन्यवाद एक गुच्छा, कम से कम मेरे पास अभी एक शुरुआती बिंदु है। – jdc0589

+0

मैं नहीं कह रहा हूं कि मेरा वर्तमान कार्यान्वयन समाप्त हो गया है, लेकिन यह किसी भी चीज़ को संभाल सकता है जिसे मैं अब तक फेंकने में सक्षम हूं। यद्यपि दक्षता पक्ष पर कुछ काम की जरूरत है। मैं वास्तव में सोचता हूं कि बिंदु पर शब्दकोष को कम करने का एकमात्र तरीका मैं स्ट्रिंग कार्यान्वयन के माध्यम से खुश हूं (जिसे अब सटीक अंतर्निहित अभिव्यक्ति संरचना में पार्स किया जाएगा)। – jdc0589

1

cletus जवाब देने के लिए जोड़ने के लिए, आप पहली बार अपने व्याकरण को परिभाषित करना चाहते होंगे।

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

 
     orexpr ::= orexpr '|' andexpr 
        | andexpr 

     andexpr ::= andexpr '&' comparison 
        | comparison 

     comparison ::= addexpr compareOp addexpr 
        | addexpr 

     addexpr ::= addexpr '+' mulexpr 
        | addexpr '-' mulexpr 
        | mulexpr 

     mulexpr ::= mulexpr '*' value 
        | mulexpr '/' value 
        | mulexpr '%' value 
        | value 

     value ::= integer 
        | float 
        | variable 
        | quotation 
        | '(' orexpr ')' 

सामान्य पुनरावर्ती वंश आप के रूप में उदाहरण के लिए, mulexpr परिभाषित करने के लिए की आवश्यकता होगी:

 
     mulexpr ::= value '*' mulexpr 
        | value '/' mulexpr 
        | value '%' mulexpr 

लेकिन इस व्याकरण के साथ समस्या यह है कि अभिव्यक्ति पेड़ इस तरह से कि के अपने आदेश में निर्माण किया जाएगा है संचालन सभी विपरीत में होंगे।

समझौता: ऊपर लिखे गए मूल व्याकरण पर विपरीत में रिकर्सिव वंश का उपयोग करें। अभिव्यक्ति को दाएं से बाएं से पार्स करें। अपने पेड़ को दायें से बाएं बनाएं। यह संचालन के आदेश को सुरक्षित रखेगा।

रिकर्सिव वंश में आप आमतौर पर प्रत्येक उत्पादन के लिए एक पार्स विधि लिखते हैं। ParseOr() विधि निम्नानुसार दिखाई दे सकती है:

 
private MyExpression parseOr(MyScanner scanner) { 
     MyExpression expression = null; 

     MyExpression rightExpr = parseAnd(scanner); 
     Token token = scanner.getCurrentToken(); 
     if (token.hasValue("|") { 
      expression = new MyExpression(); 
      expression.setOperator(OR); 
      Token nextToken = scanner.getNextToken(); // remember, this is scanning in reverse 
      MyExpression leftExpression = parseOr(scanner); 
      expression.setLeft(leftExpression); 
      expression.setRight(rightExpression); 
     } 
     else { 
      expression = rightExpression; 
     } 
     return expression; 
    } 

1

सभी युक्तियों के लिए धन्यवाद। मैंने फैसला किया कि इनमें से अधिकतर मुझे आवश्यकतानुसार अधिक रास्ता था, इसलिए मैंने इसे व्यवस्थित समूहों में लाने के लिए नरक को फिर से निकाला, जिसे मैं कोड की 20-30 पंक्तियों में आसानी से पार्स कर सकता था।

Ive स्ट्रिंग LambdaExpression इंटरफ़ेस लगभग के साथ-साथ धाराप्रवाह इंटरफ़ेस, केवल एक या दो छोटी बग काम कर रहा है।

मैं शायद इसे मजेदार बनाने के लिए थोड़ा सा विकास कर रहा हूं, लेकिन यह वास्तव में लगभग 9 0% प्रतिबिंब आधारित होने के कारण वास्तव में उपयोग करने में अक्षम है।

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