ANTLR: Basit bir örnek var mı?


230

ANTLR ile başlamak istiyorum, ancak antlr.org sitesindeki örnekleri gözden geçirmek için birkaç saat geçirdikten sonra , Java sürecinin dilbilgisini net bir şekilde anlayamıyorum.

Basit bir örnek var mı, ayrıştırıcı tanımından geçerek ve Java kaynak koduna kadar giden ANTLR ile uygulanan dört işlemli bir hesap makinesi gibi bir şey var mı?


2
Bu kesin örnek Antlr'ın sitesinde bir eğitim olarak kullanılıyor, son kontrol ettim.
Cory Petosky

1
@Cory Petosky: bağlantınızı sağlayabilir mi?
Eli

Bir video eğitiminin ilk bölümlerini ANTLR'de yayınladım. Bkz. Javadude.com/articles/antlr3xtut Umarım faydalı bulursunuz!
Scott Stanchfield

2
Ben de aramanızı paylaşıyorum.
Paul Draper

1
ANTLR 4 için en iyi cevap Parr'ın "The Definitive ANTLR 4 Reference" kitabını satın almaktır.
james.garriss

Yanıtlar:


447

Not : Bu cevap ANTLR3 içindir ! Bir ANTLR4 örneği arıyorsanız , bu soru ve cevap , ANTLR4 kullanarak basit bir ifade ayrıştırıcısının ve değerlendiricinin nasıl oluşturulacağını gösterir .


Önce bir dilbilgisi oluşturursunuz. Aşağıda, 4 temel matematik operatörü kullanılarak oluşturulan ifadeleri değerlendirmek için kullanabileceğiniz küçük bir dilbilgisi bulunmaktadır: +, -, * ve /. Ayrıca parantez kullanarak ifadeleri gruplandırabilirsiniz.

Bu dilbilgisinin çok basit bir dil olduğuna dikkat edin: yalnızca iki eksiklik belirtmek için tekli işleçleri (eksi: -1 + 9) veya .99 (ondalık sayı olmadan) ondalık sayıları işlemez. Bu sadece kendiniz üzerinde çalışabileceğiniz bir örnektir.

Exp.g gramer dosyasının içeriği :

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

(Ayrıştırıcı kuralları küçük harfle başlar ve lexer kuralları büyük harfle başlar)

Dilbilgisini oluşturduktan sonra, ondan bir ayrıştırıcı ve lexer oluşturmak isteyeceksiniz. ANTLR kavanozunu indirin ve dilbilgisi dosyanızla aynı dizinde saklayın.

Kabuk / komut isteminizde aşağıdaki komutu yürütün:

java -cp antlr-3.2.jar org.antlr.Tool Exp.g

Herhangi bir hata mesajı ve dosyaları üretmek olmamalıdır ExpLexer.java , ExpParser.java ve Exp.tokens şimdi oluşturulmalıdır.

Her şeyin düzgün çalışıp çalışmadığını görmek için şu test sınıfını oluşturun:

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();
    }
}

ve derleyin:

// *nix/MacOS
javac -cp .:antlr-3.2.jar ANTLRDemo.java

// Windows
javac -cp .;antlr-3.2.jar ANTLRDemo.java

ve sonra çalıştırın:

// *nix/MacOS
java -cp .:antlr-3.2.jar ANTLRDemo

// Windows
java -cp .;antlr-3.2.jar ANTLRDemo

Her şey yolunda giderse, konsola hiçbir şey yazdırılmıyor. Bu, ayrıştırıcının herhangi bir hata bulamadığı anlamına gelir. Eğer değiştirdiğinizde "12*(5-6)"içine "12*(5-6"ve daha sonra yeniden derleme ve çalıştırın, aşağıdakileri orada basılmış edilmelidir:

line 0:-1 mismatched input '<EOF>' expecting ')'

Tamam, şimdi dilbilgisine biraz Java kodu eklemek istiyoruz, böylece ayrıştırıcı aslında yararlı bir şey yapıyor. Ekleme kod yerleştirilmesiyle yapılabilir {ve }bunun içindeki bazı düz Java koduyla dilbilgisi içeride.

Ama önce: dilbilgisi dosyasındaki tüm ayrıştırıcı kuralları ilkel bir çift değer döndürmelidir. returns [double value]Her kuraldan sonra ekleyerek bunu yapabilirsiniz :

grammar Exp;

eval returns [double value]
    :    additionExp
    ;

additionExp returns [double value]
    :    multiplyExp 
         ( '+' multiplyExp 
         | '-' multiplyExp
         )* 
    ;

// ...

Bu da çok az açıklama gerektirir: her kuralın iki kat değer döndürmesi beklenir. Şimdi double valuebir kod bloğunun içinden dönüş değeri (düz bir Java kod bloğu içinde DEĞİLDİR) ile "etkileşim kurmak" {...}için, önüne bir dolar işareti eklemeniz gerekir 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); }
    ;

// ...

İşte dilbilgisi ama şimdi Java kodu eklendi:

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

ve evalkuralımız şimdi bir çift döndürdüğünden, ANTLRDemo.java dosyanızı şu şekilde değiştirin:

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

Tekrar (yeniden) dilbilginizden (1) yeni bir lexer ve ayrıştırıcı oluşturun, tüm sınıfları (2) derleyin ve ANTLRDemo (3) komutunu çalıştırın:

// *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

ve şimdi 12*(5-6)konsolunuza yazdırılan ifadenin sonucunu göreceksiniz !

Tekrar: Bu çok kısa bir açıklama. ANTLR wiki'ye göz atmanızı ve bazı eğiticileri okumanızı ve / veya az önce yayınladığım şeylerle biraz oynamanızı tavsiye ederim.

İyi şanslar!

DÜZENLE:

Bu yazı , yukarıdaki Map<String, Double>ifadenin değişkenleri tutan bir sağlanabilmesi için yukarıdaki örneğin nasıl genişletileceğini gösterir .

Bu kodun Antlr'ın güncel bir sürümüyle (Haziran 2014) çalışmasını sağlamak için birkaç değişiklik yapmam gerekiyordu. ANTLRStringStreamolmak için gerekli ANTLRInputStream, değişim için gerekli döndürülen değer parser.eval()için parser.eval().value, ben kaldırmak için gerekli WSnitelik değerleri gibi çünkü sonunda maddesini $channelartık lexer eylemlere görünmesine izin verilir.


1
Gerçekleşmeler nerede parser.eval()olur? BURADA veya ANTLR3 Wiki'de bu belli değil!

1
@ Marcrod, hata, üzgünüm, seni gerçekten anlamıyorum. evala döndüren bir ayrıştırıcı kuralıdır double. Yani bir var eval()bir örneğine arayabilirler yöntem ExpParserben gösterildiği gibi, ANTLRDemo.main(...). Bir lexer / ayrıştırıcı oluşturduktan sonra dosyayı açın ve a döndüren bir yöntem ExpParser.javaolduğunu göreceksiniz . eval()double
Bart Kiers

@Bart Bunu bir haftadır araştırıyorum - bu, ilk kez çalışacak kadar ayrıntılı ve eksiksiz olan ve anladığımı düşünüyorum ilk örnek. Neredeyse vazgeçmiştim. Teşekkürler!
Vineel Shah

13

Gabriele Tomassetti'nin ANTLR mega eğitimi çok faydalı

Dilbilgisi örnekleri, farklı dillerde (Java, JavaScript, C # ve Python) ziyaretçi örnekleri ve daha birçok şey var. Şiddetle tavsiye edilir.

EDIT: Gabriele Tomassetti'nin ANTLR'deki diğer faydalı makaleleri


Harika bir öğretici!
Manish Patel

Antlr artık hedef dil olarak cpp'ye de sahip. Cpp üzerinde örnek ile herhangi bir öğreticiler var mı?
vineeshvs

Aynı adam C ++ tomassetti.me/getting-started-antlr-cpp ANTLR için bir öğretici yaptı Sanırım burada veya mega öğretici
yalnız

7

Antlr 4 için java kodu oluşturma süreci aşağıdadır: -

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

Sınıf yolundaki kavanoz adınızı buna göre güncelleyin.


2

At https://github.com/BITPlan/com.bitplan.antlr bazı yararlı yardımcı sınıflar ve birkaç komple örneklerle bir ANTLR java kütüphanesi bulabilirsiniz. Maven ile kullanılmaya hazırdır ve eğer tutulma ve maven isterseniz.

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

çarpma ve ekleme işlemleri yapabilen basit bir İfade dilidir. https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/TestExpParser.java bunun için karşılık gelen birim testlerine sahiptir.

https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/main/antlr4/com/bitplan/iri/IRIParser.g4 , üç bölüme ayrılmış bir IRI ayrıştırıcısıdır:

  1. ayrıştırıcı dilbilgisi
  2. lexer dilbilgisi
  3. içe aktarılmış LexBasic dilbilgisi

https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/TestIRIParser.java bunun için birim testlerine sahiptir.

Şahsen ben bunu düzeltmek için en zor kısmı buldum. Bkz. Http://wiki.bitplan.com/index.php/ANTLR_maven_plugin

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

önceki bir sürümde ANTLR4'ün performans sorunu için oluşturulmuş üç örnek daha içerir. Bu arada, https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/TestIssue994.java'nın gösterdiği gibi bu sorunlar düzeltildi .


2

4.7.1 sürümü biraz farklıydı: içe aktarma için:

import org.antlr.v4.runtime.*;

ana segment için - CharStreams'e dikkat edin:

CharStream in = CharStreams.fromString("12*(5-6)");
ExpLexer lexer = new ExpLexer(in);
CommonTokenStream tokens = new CommonTokenStream(lexer);
ExpParser parser = new ExpParser(tokens);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.