2009-12-18 16 views
192

मैं एएनटीएलआर के साथ शुरू करना चाहता हूं, लेकिन antlr.org साइट पर उदाहरणों की समीक्षा करने में कुछ घंटों खर्च करने के बाद, मुझे अभी भी जावा प्रक्रिया में व्याकरण की स्पष्ट समझ नहीं मिल सकती है।एएनटीएलआर: क्या कोई साधारण उदाहरण है?

क्या कोई साधारण उदाहरण है, एएनटीएलआर के साथ लागू चार-ऑपरेशंस कैलक्यूलेटर जैसे कि पार्सर परिभाषा के माध्यम से जा रहा है और जावा स्रोत कोड के लिए सभी तरह से?

+2

यह सटीक उदाहरण Antlr की साइट पर ट्यूटोरियल के रूप में उपयोग किया जाता है, आखिरी मैंने चेक किया था। –

+1

@Cory Petosky: क्या आप लिंक की आपूर्ति कर सकते हैं? – Eli

+2

मैं भी आपकी खोज साझा करता हूं। –

उत्तर

393

आप पहले व्याकरण बनाते हैं। नीचे एक छोटा व्याकरण है जिसका उपयोग आप 4 मूल गणित ऑपरेटरों का उपयोग करके बनाए गए अभिव्यक्तियों का मूल्यांकन करने के लिए कर सकते हैं: +, -, * और /। आप कोष्ठक का उपयोग करके अभिव्यक्तियों को समूहबद्ध भी कर सकते हैं।

ध्यान दें कि यह व्याकरण केवल एक बहुत ही बुनियादी है: यह केवल दो कमियों का नाम देने के लिए यूनरी ऑपरेटरों (शून्य में: -1 + 9) या decimals जैसे .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;} 
    ; 

(पार्सर नियम एक लोअर केस अक्षर से शुरू, और lexer नियम बड़े अक्षर से शुरू)

के बाद व्याकरण बनाते हुए, आप इससे एक पार्सर और लेक्सर उत्पन्न करना चाहते हैं। ANTLR jar डाउनलोड करें और इसे अपनी व्याकरण फ़ाइल के समान निर्देशिका में संग्रहीत करें।

अपने खोल/कमांड प्रॉम्प्ट पर निम्न आदेश निष्पादित करें:

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)" बदलने और उसके बाद पुन: संयोजित और इसे चलाने के लिए, वहाँ मुद्रित किया जाना चाहिए निम्नलिखित:

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 
    } 
} 

फिर (री) एक ताजा lexer और अपने व्याकरण से पार्सर उत्पन्न (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 wiki ब्राउज़ करने के लिए प्रोत्साहित करता हूं और कुछ ट्यूटोरियल पढ़ता हूं और/या जो मैंने अभी पोस्ट किया है उसके साथ थोड़ा सा खेलते हैं।

शुभकामनाएं!

संपादित करें:

This post दिखाता है कि ऊपर के उदाहरण का विस्तार करने के तो एक Map<String, Double> प्रदान किया जा सकता है कि कि प्रदान की अभिव्यक्ति में चर आयोजित करता है।

और यह Q&A दर्शाता है कि एएनटीएलआर 4 का उपयोग करके एक सरल अभिव्यक्ति पार्सर और मूल्यांकनकर्ता कैसे बनाया जाए।

इस कोड को एंटरल (जून 2014) के वर्तमान संस्करण के साथ काम करने के लिए मुझे कुछ बदलाव करने की आवश्यकता है। ANTLRStringStream को ANTLRInputStream बनने की आवश्यकता है, लौटा हुआ मूल्य parser.eval() से parser.eval().value में बदलने के लिए आवश्यक है, और मुझे अंत में WS खंड को हटाने की आवश्यकता है, क्योंकि $channel जैसे विशेषता मानों को अब लेक्सर क्रियाओं में प्रकट होने की अनुमति नहीं है। classpath में अपने जार नाम तदनुसार

java -cp antlr-4.5.3-complete.jar org.antlr.v4.Tool Exp.g 

अपडेट -:

+1

'parser.eval()' के कार्यान्वयन कहां होते हैं? यह यहां या एएनटीएलआर 3 विकी पर स्पष्ट नहीं है! –

+1

@Jarrod, गलती, क्षमा करें, मैं वास्तव में आपको समझ में नहीं आता। 'eval' एक पार्सर नियम है जो 'डबल' देता है। तो एक 'eval()' विधि है जिसे आप 'ExpParser' के उदाहरण पर कॉल कर सकते हैं, जैसा कि मैंने' ANTLRDemo.main (...) 'में प्रदर्शित किया था। लेक्सर/पार्सर उत्पन्न करने के बाद, बस 'ExpParser.java' फ़ाइल खोलें और आप देखेंगे कि 'eval()' विधि 'डबल' लौटा रही है। –

+0

@ बार्ट मैं इसे एक हफ्ते तक शोध कर रहा हूं - यह पहला उदाहरण है जो वास्तव में विस्तृत था और पहली बार काम करने के लिए पर्याप्त था और मुझे लगता है कि मैं समझता हूं। मैंने लगभग छोड़ दिया था। धन्यवाद! –

7

antlr 4 जावा कोड निर्माण की प्रक्रिया के लिए नीचे है।

1

https://github.com/BITPlan/com.bitplan.antlr पर आपको कुछ उपयोगी सहायक कक्षाओं और कुछ पूर्ण उदाहरणों के साथ एक एएनटीएलआर जावा लाइब्रेरी मिलेगी। यह मेवेन के साथ उपयोग करने के लिए तैयार है और यदि आपको ग्रहण और मैवेन पसंद है।

https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/main/antlr4/com/bitplan/exp/Exp.g4

एक सरल अभिव्यक्ति भाषा कि गुणा करना और संचालन जोड़ सकते हैं। https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/TestExpParser.java इसके लिए इसी इकाई परीक्षण है।

  1. पार्सर व्याकरण
  2. lexer व्याकरण
  3. आयातित LexBasic व्याकरण

https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/TestIRIParser.java है इकाई के लिए परीक्षण:

https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/main/antlr4/com/bitplan/iri/IRIParser.g4 एक IRI पार्सर कि तीन भागों में विभाजित किया गया है यह।

व्यक्तिगत रूप से मुझे यह सही पाने के लिए सबसे कठिन हिस्सा मिला। देखें http://wiki.bitplan.com/index.php/ANTLR_maven_plugin

https://github.com/BITPlan/com.bitplan.antlr/tree/master/src/main/antlr4/com/bitplan/expr

तीन और उदाहरण है कि पिछले संस्करण में ANTLR4 के एक प्रदर्शन जारी करने के लिए बनाया गया है शामिल हैं। इस बीच इस मुद्दे को टेस्टकेस https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/TestIssue994.java शो के रूप में तय किया गया है।

3

मेरे लिए इस ट्यूटोरियल काफी मददगार था: https://tomassetti.me/antlr-mega-tutorial

यह व्याकरण उदाहरण है, अलग अलग भाषाओं में आगंतुकों का उदाहरण (जावा, जावास्क्रिप्ट, C# और पायथन) और कई अन्य चीजों। अत्यधिक सिफारिशित।

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