2010-02-09 16 views
5

मैं एएनटीएलआर सीखने की कोशिश कर रहा हूं और साथ ही साथ इसे एक मौजूदा परियोजना के लिए उपयोग कर रहा हूं।एएनटीएलआर के साथ कॉमनटोकनस्ट्रीम में टोकन के पाठ को मैं कैसे संशोधित कर सकता हूं?

मैं उस बिंदु पर पहुंच गया हूं जहां मैं लेक्सर को कोड के एक हिस्से पर चला सकता हूं और इसे कॉमनटोकनस्ट्रीम पर आउटपुट कर सकता हूं। यह ठीक काम कर रहा है, और मैंने सत्यापित किया है कि स्रोत टेक्स्ट उचित टोकन में तोड़ा जा रहा है।

अब, मैं इस स्ट्रीम में कुछ टोकन के पाठ को संशोधित करने में सक्षम होना चाहता हूं, और अब संशोधित स्रोत कोड प्रदर्शित करना चाहता हूं।

उदाहरण के लिए मैं कोशिश की है:

import org.antlr.runtime.*; 
import java.util.*; 

public class LexerTest 
{ 
    public static final int IDENTIFIER_TYPE = 4; 

    public static void main(String[] args) 
    { 
    String input = "public static void main(String[] args) { int myVar = 0; }"; 
    CharStream cs = new ANTLRStringStream(input); 


     JavaLexer lexer = new JavaLexer(cs); 
     CommonTokenStream tokens = new CommonTokenStream(); 
     tokens.setTokenSource(lexer); 

     int size = tokens.size(); 
     for(int i = 0; i < size; i++) 
     { 
      Token token = (Token) tokens.get(i); 
      if(token.getType() == IDENTIFIER_TYPE) 
      { 
       token.setText("V"); 
      } 
     } 
     System.out.println(tokens.toString()); 
    } 
} 

मैं स्ट्रिंग शाब्दिक "वी" करने के लिए सभी पहचानकर्ता टोकन का पाठ सेट करने के लिए कोशिश कर रहा हूँ।

  1. टोकन का पाठ करने के लिए अपने परिवर्तनों को नहीं देखा जा सकता है जब मैं tokens.toString फोन क्यों कर रहे हैं()?

  2. मुझे विभिन्न टोकन प्रकार आईडी कैसे पता चल रहा है? मैं अपने डीबगर के साथ चला गया और देखा कि पहचानकर्ता टोकन के लिए आईडी "4" थी (इसलिए शीर्ष पर मेरा निरंतर)। लेकिन मैं अन्यथा कैसे जानूंगा? टोकन नाम में टोकन प्रकार आईडी को मैप करने का कोई अन्य तरीका है?


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

एक बात है कि मुझे के लिए महत्वपूर्ण है मैं टोकन उनके मूल आरंभ और अंत चरित्र पदों के लिए के लिए चाहते हैं। यही है, मैं नहीं चाहता कि वे परिवर्तनीय नामों के साथ अपनी नई स्थिति को प्रतिबिंबित करें "वी" में बदल दें। ऐसा इसलिए है कि मुझे पता है कि टोकन मूल स्रोत टेक्स्ट में कहां थे।

+0

बस सोच रहा है - यह एक आवश्यकता है कि आप ANTLR का उपयोग करें इसके लिए? – cowboydan

उत्तर

5

एएनटीएलआर के पास इसकी व्याकरण फ़ाइल में ऐसा करने का एक तरीका है।

मान लीजिए कि आप एक स्ट्रिंग को पार्स कर रहे हैं जिसमें कॉमा के द्वारा सीमित संख्याएं और स्ट्रिंग शामिल हैं। एक व्याकरण इस तरह दिखेगा:

grammar Foo; 

parse 
    : value (',' value)* EOF 
    ; 

value 
    : Number 
    | String 
    ; 

String 
    : '"' (~('"' | '\\') | '\\\\' | '\\"')* '"' 
    ; 

Number 
    : '0'..'9'+ 
    ; 

Space 
    : (' ' | '\t') {skip();} 
    ; 

यह सब आपको परिचित दिखना चाहिए। मान लें कि आप सभी पूर्णांक मानों के चारों ओर स्क्वायर ब्रैकेट को लपेटना चाहते हैं। यहाँ है कि कैसे करना है:

grammar Foo; 

options {output=template; rewrite=true;} 

parse 
    : value (',' value)* EOF 
    ; 

value 
    : n=Number -> template(num={$n.text}) "[<num>]" 
    | String 
    ; 

String 
    : '"' (~('"' | '\\') | '\\\\' | '\\"')* '"' 
    ; 

Number 
    : '0'..'9'+ 
    ; 

Space 
    : (' ' | '\t') {skip();} 
    ; 

जैसा कि आप देख, मैं शीर्ष पर कुछ options जोड़ दिया है, और value पार्सर शासन में Number के बाद रीराइट नियम (-> के बाद सब कुछ) जोड़ा गया।

अब यह सब का परीक्षण, संकलन और इस वर्ग को चलाने के लिए:

import org.antlr.runtime.*; 

public class FooTest { 
    public static void main(String[] args) throws Exception { 
    String text = "12, \"34\", 56, \"a\\\"b\", 78"; 
    System.out.println("parsing: "+text); 
    ANTLRStringStream in = new ANTLRStringStream(text); 
    FooLexer lexer = new FooLexer(in); 
    CommonTokenStream tokens = new TokenRewriteStream(lexer); // Note: a TokenRewriteStream! 
    FooParser parser = new FooParser(tokens); 
    parser.parse(); 
    System.out.println("tokens: "+tokens.toString()); 
    } 
} 

जो पैदा करता है:

parsing: 12, "34", 56, "a\"b", 78 
tokens: [12],"34",[56],"a\"b",[78] 
2

lexer में टेक्स्ट बदलने के अन्य दिए गए उदाहरण में अच्छी तरह से काम करता है यदि आप चाहते हैं वैश्विक स्तर पर सभी परिस्थितियों में टेक्स्ट को प्रतिस्थापित करें, हालांकि आप अक्सर कुछ स्थितियों के दौरान टोकन के टेक्स्ट को प्रतिस्थापित करना चाहते हैं।

टोकन रिवाइटस्ट्रीम का उपयोग करके आप केवल कुछ संदर्भों के दौरान पाठ को बदलने की लचीलापन की अनुमति देते हैं।

यह आपके द्वारा उपयोग किए जा रहे टोकन स्ट्रीम क्लास के उप-वर्ग का उपयोग करके किया जा सकता है। CommonTokenStream कक्षा का उपयोग करने के बजाय आप TokenRewriteStream का उपयोग कर सकते हैं।

तो आपके पास टोकन रीवाइटस्ट्रीम लेक्सर का उपभोग करेगा और फिर आप अपना पार्सर चलाएंगे।

/** Convert "int foo() {...}" into "float foo();" */ 
function 
: 
{ 
    RefTokenWithIndex t(LT(1)); // copy the location of the token you want to replace 
    engine.replace(t, "float"); 
} 
type id:ID LPAREN (formalParameter (COMMA formalParameter)*)? RPAREN 
    block[true] 
; 

यहाँ हम टोकन पूर्णांक है कि हम पाठ नाव के साथ मिलान किया बदल दिया है:

अपने व्याकरण में आम तौर पर आप इस तरह प्रतिस्थापन करना चाहते हैं। स्थान जानकारी संरक्षित है लेकिन यह "मिलान" पाठ बदल दिया गया है।

आपके टोकन स्ट्रीम को जांचने के बाद आप पहले के समान कोड का उपयोग करेंगे।

+0

जानकारी के लिए धन्यवाद। क्या आपको कोई विचार है कि व्यक्तिगत टोकन पर सेटटेक्स्ट को कॉल क्यों नहीं किया गया? – mmcdole

+0

@ सिमुकल, आईडी आप 'कॉमनटोकनस्ट्रीम' के बजाय 'टोकन रीवाइटस्ट्रीम' का उपयोग करने का प्रयास करते हैं? –

+0

@ सिमुकाल, मैंने एंटरलर के लिए जावा स्रोत में खोला नहीं है, क्योंकि मैं आमतौर पर सी ++ का उपयोग करता हूं, लेकिन मुझे लगता है कि आप टोकन स्ट्रीम की एक प्रति संशोधित कर रहे हैं, न कि वास्तविक स्ट्रीम। – chollida

2

एएनटीएलआर 4 में पार्स पेड़ श्रोताओं और टोकनस्ट्रीम रेवाइटर (नाम अंतर को नोट करें) का उपयोग करके एक नई सुविधा है जिसका उपयोग पेड़ों को देखने या बदलने के लिए किया जा सकता है। (उत्तर ANTLR 3 के लिए लागू TokenRewriteStream सुझाव और ANTLR 4. साथ काम नहीं करेंगे)

ANTL4 में एक XXXBaseListener वर्ग व्याकरण में प्रवेश करने और बाहर निकलने प्रत्येक गैर टर्मिनल नोड (जैसे enterClassDeclaration के लिए कॉलबैक के साथ आप के लिए उत्पन्न होता है())।

आप दो तरह से श्रोता का उपयोग कर सकते हैं:

1) एक पर्यवेक्षक के रूप में - जैसे - बस मनमाने ढंग से उत्पादन इनपुट पाठ से संबंधित निर्माण करने के लिए तरीकों अधिभावी तक enterClassDeclaration() को ओवरराइड करें और अपने प्रोग्राम में घोषित प्रत्येक वर्ग के लिए एक लाइन आउटपुट करें।

2) मूल पाठ को संशोधित करने के लिए टोकन रीवाइटस्ट्रीम का उपयोग करके एक ट्रांसफार्मर के रूप में। ऐसा करने के लिए आप कॉलबैक विधियों में संशोधन (जोड़ने, हटाने, प्रतिस्थापित करने) टोकन बनाने के लिए पुनःलेखक का उपयोग करते हैं और आप संशोधित पाठ को आउटपुट करने के लिए पुनःलेखक और अंत का उपयोग करते हैं।

कैसे परिवर्तनों करने के लिए का एक उदाहरण के लिए ANTL4 किताब से निम्नलिखित उदाहरण देखें:

https://github.com/mquinn/ANTLR4/blob/master/book_code/tour/InsertSerialIDListener.java

और

https://github.com/mquinn/ANTLR4/blob/master/book_code/tour/InsertSerialID.java

+0

गिटहब रेपो के लिंक अब मर चुके हैं। –