দ্রষ্টব্য : এই উত্তরটি এএনটিএলআর 3 এর জন্য ! আপনি যদি কোনও এএনটিএলআর 4 উদাহরণ সন্ধান করছেন, তবে এই প্রশ্নোত্তর এএনটিএলআর 4 ব্যবহার করে কীভাবে একটি সহজ এক্সপ্রেশন পার্সার এবং মূল্যায়নকারী তৈরি করবেন তা প্রদর্শন করে ।
আপনি প্রথমে একটি ব্যাকরণ তৈরি করুন। নীচে একটি ছোট ব্যাকরণ যা আপনি 4 বেসিক গণিত অপারেটরগুলি ব্যবহার করে তৈরি করা হয়েছে তা প্রকাশের মূল্যায়ন করতে ব্যবহার করতে পারেন: +, -, * এবং /। আপনি প্রথম বন্ধনী ব্যবহার করে গোষ্ঠীভুক্তিগুলিও গোষ্ঠীভুক্ত করতে পারেন।
মনে রাখবেন যে এই ব্যাকরণটি কেবলমাত্র একটি প্রাথমিক একটি: এটি অ্যারারি অপারেটরগুলি (বিয়োগ: -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;}
;
(পার্সার বিধিগুলি ছোট হাতের অক্ষর দিয়ে শুরু হয় এবং লেক্সারের বিধিগুলি মূলধনির সাথে শুরু হয়)
ব্যাকরণ তৈরির পরে, আপনি এটি থেকে একটি পার্সার এবং লেক্সার তৈরি করতে চান। এএনটিএলআর জারটি ডাউনলোড করুন এবং আপনার ব্যাকরণ ফাইলের মতো একই ডিরেক্টরিতে এটি সংরক্ষণ করুন।
আপনার শেল / কমান্ড প্রম্পটে নিম্নলিখিত কমান্ডটি প্রয়োগ করুন:
java -cp antlr-3.2.jar org.antlr.Tool Exp.g
এটিতে কোনও ত্রুটি বার্তা তৈরি করা উচিত নয় এবং এক্সপ্লেক্সার.জভা , এক্সপ্রেসার.জভা এবং এক্সপ্রেস টোকেন ফাইলগুলি এখন উত্পন্ন করা উচিত।
এটি সব ঠিকঠাক কাজ করে কিনা তা দেখতে এই পরীক্ষার শ্রেণিটি তৈরি করুন:
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
নিয়ম এখন দ্বিগুণ ফেরতছে তাই আপনার এএনটিএলআরডি ডেমো.জভাটিকে এতে পরিবর্তন করুন:
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) সংকলন করুন এবং এএনটিএলআরডেমো (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>
সরবরাহিত অভিব্যক্তিতে ভেরিয়েবল ধারণ করে এমন একটি সরবরাহ করা যায়।
এই কোডটি অ্যান্টলারের বর্তমান সংস্করণ (জুন ২০১৪) এর সাথে কাজ করার জন্য আমার কয়েকটি পরিবর্তন করা দরকার। ANTLRStringStream
পরিণত করা প্রয়োজন ANTLRInputStream
, থেকে পরিবর্তন করা প্রয়োজন ফিরে মান parser.eval()
থেকে parser.eval().value
, এবং আমি অপসারণ করা প্রয়োজন WS
শেষে দফা, কারণ অ্যাট্রিবিউট মান যেমন $channel
আর lexer কর্মের মধ্যে প্রদর্শিত অনুমতি দেওয়া হয়।