سؤال

أرغب في البدء باستخدام ANTLR، ولكن بعد قضاء بضع ساعات في مراجعة الأمثلة في antlr.org الموقع، ما زلت لا أستطيع الحصول على فهم واضح للقواعد النحوية لعملية جافا.

هل هناك بعض الأمثلة البسيطة، مثل آلة حاسبة ذات أربع عمليات يتم تنفيذها باستخدام ANTLR من خلال تعريف المحلل اللغوي وصولاً إلى كود مصدر Java؟

هل كانت مفيدة؟

المحلول

ملحوظة:هذه الإجابة ل ANTLR3!إذا كنت تبحث عن ANTLR4 سبيل المثال، ثم هذا سؤال وجواب يوضح كيفية إنشاء محلل تعبير بسيط ومقيم باستخدام ANTLR4.


عليك أولا إنشاء القواعد.يوجد أدناه قواعد نحوية صغيرة يمكنك استخدامها لتقييم التعبيرات التي تم إنشاؤها باستخدام عوامل الرياضيات الأساسية الأربعة:+، -، * و /.يمكنك أيضًا تجميع التعبيرات باستخدام الأقواس.

لاحظ أن هذه القواعد هي مجرد قاعدة أساسية جدًا:لا يتعامل مع العوامل الأحادية (الطرح في:-1+9) أو الكسور العشرية مثل .99 (بدون رقم بادئ)، على سبيل المثال لا الحصر اثنين من أوجه القصور.هذا مجرد مثال يمكنك العمل عليه بنفسك.

إليك محتويات الملف النحوي إكسب.ز:

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 وقم بتخزينه في نفس الدليل مثل ملف القواعد الخاص بك.

قم بتنفيذ الأمر التالي على Shell/موجه الأوامر الخاص بك:

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 ')'

حسنًا، نريد الآن إضافة القليل من تعليمات Java البرمجية إلى القواعد النحوية حتى يتمكن المحلل اللغوي من القيام بشيء مفيد بالفعل.يمكن إضافة الكود عن طريق وضعه { و } داخل القواعد الخاصة بك مع بعض أكواد Java البسيطة بداخلها.

لكن اولا:يجب أن تقوم كافة قواعد المحلل اللغوي في الملف النحوي بإرجاع قيمة مزدوجة بدائية.يمكنك القيام بذلك عن طريق إضافة returns [double value] بعد كل قاعدة:

grammar Exp;

eval returns [double value]
    :    additionExp
    ;

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

// ...

والذي يحتاج إلى القليل من الشرح:من المتوقع أن تقوم كل قاعدة بإرجاع قيمة مزدوجة.الآن "التفاعل" مع القيمة المرجعة double value (وهو ليس داخل كتلة تعليمات برمجية Java عادية {...}) من داخل مجموعة التعليمات البرمجية، ستحتاج إلى إضافة علامة الدولار أمامها 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); }
    ;

// ...

إليك القواعد النحوية ولكن الآن مع إضافة كود Java:

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) المطبوعة على وحدة التحكم الخاصة بك!

مرة أخرى:وهذا شرح مختصر جداً.أنصحك بتصفح أنتلر ويكي وقراءة بعض البرامج التعليمية و/أو اللعب قليلاً بما نشرته للتو.

حظ سعيد!

يحرر:

هذا المشنور يوضح كيفية توسيع المثال أعلاه بحيث يكون أ Map<String, Double> يمكن توفيرها التي تحتوي على المتغيرات في التعبير المقدم.

لكي يعمل هذا الرمز مع الإصدار الحالي من Antlr (يونيو 2014)، كنت بحاجة إلى إجراء بعض التغييرات. ANTLRStringStream اللازمة لتصبح ANTLRInputStream, ، القيمة التي تم إرجاعها مطلوبة للتغيير منها parser.eval() ل parser.eval().value, ، وكنت بحاجة لإزالة WS جملة في النهاية، لأن قيم السمات مثل $channel لم يعد مسموحًا بالظهور في إجراءات المعجم.

نصائح أخرى

ANTLR ميجا البرنامج التعليمي بقلم غابرييل توماسيتي مفيد جدًا

يحتوي على أمثلة نحوية وأمثلة للزوار بلغات مختلفة (Java وJavaScript وC# وPython) وأشياء أخرى كثيرة.ينصح به بشده.

يحرر:مقالات مفيدة أخرى كتبها غابرييل توماسيتي على ANTLR

بالنسبة لـ Antlr 4، تكون عملية إنشاء كود Java أدناه: -

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

قم بتحديث اسم الجرة الخاص بك في classpath وفقًا لذلك.

في https://github.com/BITPlan/com.bitplan.antlr ستجد مكتبة جافا ANTLR مع بعض فئات المساعدة المفيدة وبعض الأمثلة الكاملة.إنه جاهز للاستخدام مع maven وإذا كنت تحب Eclipse وmaven.

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 لديه اختبارات الوحدة المقابلة لذلك.

https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/main/antlr4/com/bitplan/iri/IRIParser.g4 هو محلل IRI تم تقسيمه إلى ثلاثة أجزاء:

  1. النحوي
  2. قواعد ليكسر
  3. قواعد LexBasic المستوردة

https://github.com/BITPlan/com.bitplan.antlr/blob/master/src/test/java/com/bitplan/antlr/TestIRIParser.javaلديه اختبارات الوحدة لذلك.

أنا شخصياً وجدت أن هذا هو الجزء الأكثر صعوبة في الحصول على حق.يرى 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 عروض.

الإصدار 4.7.1 كان مختلفًا قليلاً:للاستيراد:

import org.antlr.v4.runtime.*;

بالنسبة للجزء الرئيسي - لاحظ CharStreams:

CharStream in = CharStreams.fromString("12*(5-6)");
ExpLexer lexer = new ExpLexer(in);
CommonTokenStream tokens = new CommonTokenStream(lexer);
ExpParser parser = new ExpParser(tokens);
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top