नोट : यह उत्तर ANTLR3 के लिए है ! यदि आप ANTLR4 उदाहरण के लिए देख रहे हैं , तो यह Q & A दर्शाता है कि कैसे एक सरल अभिव्यक्ति पार्सर, और ANTLR4 का उपयोग करके मूल्यांकनकर्ता बनाया जाए ।
आप पहले एक व्याकरण बनाएँ। नीचे एक छोटा व्याकरण है जिसे आप 4 मूल गणित ऑपरेटरों: +, -, * और / का उपयोग करके बनाए गए भावों का मूल्यांकन करने के लिए उपयोग कर सकते हैं। आप कोष्ठक का उपयोग करके समूह के भाव भी कर सकते हैं।
ध्यान दें कि यह व्याकरण सिर्फ एक बहुत ही बुनियादी है: यह सिर्फ दो कमियों को दूर करने के लिए यूनी ऑपरेटर्स (: -1 + 9 में माइनस) या .99 (जैसे एक प्रमुख संख्या के बिना) को हैंडल नहीं करता है। यह सिर्फ एक उदाहरण है जो आप खुद पर काम कर सकते हैं।
यहां व्याकरण फ़ाइल की सामग्री Exp.g :
grammar Exp;
/* This will be the entry point of our parser. */
eval
: additionExp
;
/* Addition and subtraction have the lowest precedence. */
additionExp
: multiplyExp
( '+' multiplyExp
| '-' multiplyExp
)*
;
/* Multiplication and division have a higher precedence. */
multiplyExp
: atomExp
( '*' atomExp
| '/' atomExp
)*
;
/* An expression atom is the smallest part of an expression: a number. Or
when we encounter parenthesis, we're making a recursive call back to the
rule 'additionExp'. As you can see, an 'atomExp' has the highest precedence. */
atomExp
: Number
| '(' additionExp ')'
;
/* A number: can be an integer value, or a decimal value */
Number
: ('0'..'9')+ ('.' ('0'..'9')+)?
;
/* We're going to ignore all white space characters */
WS
: (' ' | '\t' | '\r'| '\n') {$channel=HIDDEN;}
;
(पार्सर नियम कम केस लेटर से शुरू होते हैं, और लेसर रूल्स कैपिटल लेटर से शुरू होते हैं)
व्याकरण बनाने के बाद, आप इससे एक पार्सर और लेक्सर उत्पन्न करना चाहेंगे। ANTLR जार डाउनलोड करें और इसे अपनी व्याकरण फ़ाइल के समान निर्देशिका में संग्रहीत करें।
अपने शेल / कमांड प्रॉम्प्ट पर निम्न कमांड निष्पादित करें:
java -cp antlr-3.2.jar org.antlr.Tool Exp.g
यह किसी भी त्रुटि संदेश का उत्पादन नहीं करना चाहिए, और ExpLexer.java , ExpParser.java और Exp.tokens फ़ाइलों को अब उत्पन्न किया जाना चाहिए।
यह देखने के लिए कि क्या यह सब ठीक से काम करता है, यह परीक्षण वर्ग बनाएं:
import org.antlr.runtime.*;
public class ANTLRDemo {
public static void main(String[] args) throws Exception {
ANTLRStringStream in = new ANTLRStringStream("12*(5-6)");
ExpLexer lexer = new ExpLexer(in);
CommonTokenStream tokens = new CommonTokenStream(lexer);
ExpParser parser = new ExpParser(tokens);
parser.eval();
}
}
और इसे संकलित करें:
// *nix/MacOS
javac -cp .:antlr-3.2.jar ANTLRDemo.java
// Windows
javac -cp .;antlr-3.2.jar ANTLRDemo.java
और फिर इसे चलाएं:
// *nix/MacOS
java -cp .:antlr-3.2.jar ANTLRDemo
// Windows
java -cp .;antlr-3.2.jar ANTLRDemo
अगर सब ठीक हो जाता है, तो कंसोल को कुछ भी नहीं छापा जा रहा है। इसका मतलब है कि पार्सर को कोई त्रुटि नहीं मिली। जब आप को बदलने "12*(5-6)"
में "12*(5-6"
recompile और फिर और इसे चलाने के लिए, वहाँ निम्नलिखित मुद्रित किया जाना चाहिए:
line 0:-1 mismatched input '<EOF>' expecting ')'
ठीक है, अब हम व्याकरण में थोड़ा जावा कोड जोड़ना चाहते हैं ताकि पार्सर वास्तव में कुछ उपयोगी हो। कोड जोड़ने {
और }
अपने व्याकरण के अंदर कुछ सादे जावा कोड के साथ रखकर और अंदर किया जा सकता है ।
लेकिन पहले: व्याकरण फ़ाइल में सभी पार्सर नियम एक आदिम दोहरा मूल्य वापस करना चाहिए। आप returns [double value]
प्रत्येक नियम को जोड़कर ऐसा कर सकते हैं :
grammar Exp;
eval returns [double value]
: additionExp
;
additionExp returns [double value]
: multiplyExp
( '+' multiplyExp
| '-' multiplyExp
)*
;
// ...
जिसे बहुत कम स्पष्टीकरण की आवश्यकता है: प्रत्येक नियम से दोहरा मूल्य वापस करने की अपेक्षा की जाती है। अब एक कोड ब्लॉक के अंदर से रिटर्न वैल्यू double value
(जो एक सादे जावा कोड ब्लॉक के अंदर नहीं है {...}
) के साथ "इंटरैक्ट" करने के लिए , आपको सामने एक डॉलर का चिह्न जोड़ना होगा value
:
grammar Exp;
/* This will be the entry point of our parser. */
eval returns [double value]
: additionExp { /* plain code block! */ System.out.println("value equals: "+$value); }
;
// ...
यहाँ व्याकरण है, लेकिन अब जावा कोड के साथ जोड़ा गया है:
grammar Exp;
eval returns [double value]
: exp=additionExp {$value = $exp.value;}
;
additionExp returns [double value]
: m1=multiplyExp {$value = $m1.value;}
( '+' m2=multiplyExp {$value += $m2.value;}
| '-' m2=multiplyExp {$value -= $m2.value;}
)*
;
multiplyExp returns [double value]
: a1=atomExp {$value = $a1.value;}
( '*' a2=atomExp {$value *= $a2.value;}
| '/' a2=atomExp {$value /= $a2.value;}
)*
;
atomExp returns [double value]
: n=Number {$value = Double.parseDouble($n.text);}
| '(' exp=additionExp ')' {$value = $exp.value;}
;
Number
: ('0'..'9')+ ('.' ('0'..'9')+)?
;
WS
: (' ' | '\t' | '\r'| '\n') {$channel=HIDDEN;}
;
और चूंकि हमारा eval
नियम अब दोहरा रिटर्न करता है, इसलिए अपने ANTLRDemo.java को इसमें बदलें:
import org.antlr.runtime.*;
public class ANTLRDemo {
public static void main(String[] args) throws Exception {
ANTLRStringStream in = new ANTLRStringStream("12*(5-6)");
ExpLexer lexer = new ExpLexer(in);
CommonTokenStream tokens = new CommonTokenStream(lexer);
ExpParser parser = new ExpParser(tokens);
System.out.println(parser.eval()); // print the value
}
}
फिर से (फिर से) अपने व्याकरण (1) से एक नया लेसर और पार्सर उत्पन्न करें, सभी वर्गों (2) को संकलित करें और ANTLRDemo (3) चलाएं:
// *nix/MacOS
java -cp antlr-3.2.jar org.antlr.Tool Exp.g // 1
javac -cp .:antlr-3.2.jar ANTLRDemo.java // 2
java -cp .:antlr-3.2.jar ANTLRDemo // 3
// Windows
java -cp antlr-3.2.jar org.antlr.Tool Exp.g // 1
javac -cp .;antlr-3.2.jar ANTLRDemo.java // 2
java -cp .;antlr-3.2.jar ANTLRDemo // 3
और अब आप 12*(5-6)
अपने कंसोल पर मुद्रित अभिव्यक्ति के परिणाम देखेंगे !
फिर से: यह एक बहुत ही संक्षिप्त विवरण है। मैं आपको ANTLR विकी ब्राउज़ करने और कुछ ट्यूटोरियल पढ़ने और / या जो मैंने अभी पोस्ट किया है, उसके साथ थोड़ा खेलने के लिए प्रोत्साहित करता हूं।
सौभाग्य!
संपादित करें:
यह पोस्ट दिखाती है कि ऊपर दिए गए उदाहरण का विस्तार कैसे किया जाए ताकि Map<String, Double>
प्रदान की जा सकने वाली अभिव्यक्ति में परिवर्तनशील हो सके।
इस कोड को एंट्र (जून 2014) के वर्तमान संस्करण के साथ काम करने के लिए मुझे कुछ बदलाव करने की आवश्यकता थी। ANTLRStringStream
बनने के लिए की जरूरत ANTLRInputStream
, से परिवर्तन करने की जरूरत दिए गए मान parser.eval()
के लिए parser.eval().value
, और मैं दूर करने के लिए की जरूरत है WS
अंत में खंड, क्योंकि विशेषता मान जैसे $channel
नहीं रह lexer कार्यों में प्रदर्शित करने की अनुमति है।