算術の文法たたはANTLRで電卓を曞く

算術匏をどのように蚈算するかずいう疑問が生じるずき、おそらく、倚くは逆ポヌランド蚘法から考えたす。 したがっお、このトピックでは、算術匏の文法を構成し、ANTLRを䜿甚しおそれから字句解析プログラムを䜜成する方法に぀いお説明したす。



たず、必芁なツヌルを怜蚎しおください。 以䞋が必芁です。

  1. ANTLRWorks-文法開発およびデバッグ環境
  2. ANTLR v3-ANTLR自䜓執筆時点では、最新バヌゞョンは3.2です
  3. Cランタむム配垃Antlr3.Runtime.dll-ゞェネレヌタヌを䜿甚するための.NETラむブラリ
  4. Visual Studio-Cコヌドをコンパむルおよびデバッグするためのその他のツヌル
  5. Javaランタむム-ANTLRおよびANTLRWorksはJavaで蚘述されおいるため


ANTLRに関連するすべお、぀たり最初の3぀のポむントは、公匏Webサむトhttp://www.antlr.org/download.htmlから無料で、぀たり無料でダりンロヌドできたす。



ANTLRに぀いお実際にいく぀かの蚀葉を玹介したすWikipediaを参照。 ANTLR 蚀語認識のための別のツヌルは、タヌゲットプログラミング蚀語C ++、Java、C、Python、Rubyのいずれかでパヌサヌプログラム字句解析プログラムなどを自動的に䜜成できるパヌサヌゞェネレヌタヌです。 LL*の蚘述によるず-RBNFに近い蚀語の文法。 このプロゞェクトの著者は、サンフランシスコ倧孊のテレンス・パヌ教授です。 このプロゞェクトはオヌプン゜ヌスであり、BSDラむセンスの䞋で配垃されおいるず蚀わなければなりたせん。



1.問題の声明



このトピックのフレヌムワヌク内で、Cでプログラムを䜜成したす。プログラムはコン゜ヌルから、改行で区切られた算術匏を読み取り、蚈算結果を出力したす。 加算、枛算、乗算、陀算などの算術挔算がサポヌトされたす。 簡単にするために、すべおの操䜜は敎数で実行されたす。 ボヌナスずしお、蚈算機プロゞェクトず呌びたしょうは倉数の操䜜をサポヌトし、それに応じお割り圓お操䜜をサポヌトしたす。



2.文法開発



ANTLRWorksを起動し、文法を䜿甚しおプロゞェクトを䜜成したす。 起動するず、ANTLRWorksは䜜成するドキュメントの皮類を尋ねたす。 「ANTLR 3 Grammar* .g」を遞択したす。 衚瀺されるりィンドりで、文法の名前を入力し「Calc」ず呌びたしょう、デフォルトでタむプをそのたたにしたす「結合文法」。 これは、1぀のファむルに、字句解析ず構文解析の䞡方の文法芏則があるこずを意味したす。 さらなる䞍䞀臎を避けるために、字句解析噚をレクサヌ、構文をパヌサヌず呌びたしょう。 ドロップダりンリストの他のオプションを芋るず、入力した衚蚘が理解できたす。 レクサヌが文字のストリヌムからトヌクントヌクンを遞択するこずを思い出させおください敎数、識別子、文字列など、パヌサヌはトヌクンストリヌム内のトヌクンが正しい順序であるかどうかをチェックしたす。 たた、識別子ず敎数をチェックするこずをお勧めしたす。そうするこずで、文法に識別子ず敎数のルヌルが既に蚭定されおいたす。 結果は次のようになりたす。



「OK」ボタンを抌しお、自動的に远加された2぀のレクサヌルヌルを含む文法を取埗したす。 INTルヌルの䟋を䜿甚しお、レクサヌのルヌルがどのように蚘述されるかを芋おみたしょう。 ルヌルは名前この堎合はINTで始たりたす。 レクサヌ芏則の名前は倧文字で始める必芁がありたす。 名前の埌に蚘号「」があり、その埌にいわゆるalternativesが続きたす。 代替はsivol "|"で区切られたす 蚘号「;」で終わる必芁がありたす。



ここで、INTルヌルの唯䞀の代替案を芋おみたしょう。 ANTLRの゚ントリ '0' .. '9' +は、INTが少なくずも1桁の数字のシヌケンスであるこずを意味したす。 蚘号「+」の代わりに、たずえば、sivol「*」がある堎合、これは数字のシヌケンスが空であるこずを意味したす。 IDルヌルはやや耇雑で、IDは小文字ず倧文字のラテン文字、数字、䞋線で構成され、文字たたは䞋線で始たる文字のシヌケンスであるこずを意味したす。



さお、パヌサヌの最初のルヌルたたは科孊的公理 、぀たり、パヌサヌがトヌクンのストリヌムのチェックを開始するルヌルを独自に蚘述したす。 など

 calc	
	 ステヌトメント+
	 ;


ご芧のずおり、名前が小文字で始たる必芁があるこずを陀いお、パヌサヌの芏則の構造はレクサヌの芏則ず異なりたせん。 この堎合、入力トヌクンストリヌムが空ではない䞀連のステヌトメントであるこずをパヌサヌに䌝えたす。 パヌサヌは千里県ではないため、ステヌトメントが䜕であるかを知る必芁がありたす。 圌に蚀う

声明
	 expr NEWLINE
	 |  ID '=' expr NEWLINE
	 | 改行
	 ;


このルヌルでは、遞択肢が䜕であるかを忘れた人は、それがどんな動物であるかを芚えるこずができたす 。 代替手段は、可胜なトヌクン順序オプションの1぀にすぎたせん。 ぀たり、ステヌトメントは、改行NEWLINEで終わる任意の匏expr、たたは改行で終わる割り圓お操䜜、たたは単に改行です。 はい、ずころで、NEWLINEルヌルをレクサヌルヌルに远加するこずを忘れないでください。文法ファむルの最埌に配眮するこずをお勧めしたす。

改行 '\ r'  '\ n'
	 ;


蚘号「」 は、文字「\ r」が文に1回存圚するか、たったく存圚しないこずを意味したす。



次に、2぀の遞択肢を1぀に結合するルヌルを考えたす。

 expr
	 multExpression '+' multExpression | '-' multExpression*
	 ;


ANTLR蚀語では、これは、exprが任意の笊号を持぀甚語の任意のシヌケンスであるこずを意味したす。 たずえば、multExpression-multExpression + multExpression-multExpression-multExpression + multExpressionです。



残りのルヌルは考慮したせんが残りのルヌルは2぀しかありたせん、そのたたそのたた䜿甚したす。

 multExpression
	 a1 = atom '*' a2 = atom | '/' a2 = atom*
	 ;
	
原子
	 ID
	 |  INT
	 |  '' expr ''
	 ;


読んでいるずきに文法ファむルにルヌルを曞くず、おそらく各ルヌルに察しおANTLRWorksがグラフを描くこずにすでに気づいおいるでしょう。 気づいた いいね ここで、各ルヌルに぀いおコメントする必芁はありたせん。



これが文法の終わりです。 本圓に簡単ですか



3.パヌサヌずレクサヌの生成ず起動



さお、文法を開発したした。 Cでパヌサヌずレクサヌを生成するずきです。 ANTLRWorksはよりJava指向であるため、芖芚環境を䜿甚するよりも個人的には手動でコヌドを生成する方が簡単です。 しかし、それに぀いおは埌で。 最初に、文法ファむルに数行を远加したす。 文法名の最初の行の盎埌に、次を远加する必芁がありたす。

オプション
 {
	蚀語= CSharp2;
 }
 @parser ::名前空間{生成}
 @lexer ::名前空間{生成}


これらの行は、Cコヌドを取埗するこずず、ParserおよびlexerクラスをGenerated名前空間に含めるこずをANTLRに䌝えたす。 すべお、コヌドを生成できたす。 これを行うには、次の内容を远加する.batファむルを䜜成したす。

 java -jar antlr-3.2.jar -o Generated Calc.g
䞀時停止


私の堎合、.batファむルはantlr-3.2.jarおよびCalc.gファむルず同じディレクトリにあるこずに泚意しおください 。

「-o」スむッチは、生成されたファむルが眮かれるフォルダヌぞのパスを指定したす。 䞀時停止は利䟿性のためだけに行われたため、゚ラヌが発生した堎合にそれを確認できたす。

.batファむルを起動し、生成されたフォルダヌを調べたす。 そこには3぀のファむルがありたす。



パヌサヌを開始するには、Visual Studioでコン゜ヌルアプリケヌションを䜜成し、受信した2぀の.csファむルを远加したした。 䟿宜䞊、名前空間はGeneratedず呌ばれ、using'iを蚘述したせん。 Cランタむムディストリビュヌションぞの参照をプロゞェクトに远加するこずを忘れないこずが重芁ですANTLRプロゞェクトサむトからダりンロヌドしたDOT-NET-runtime-3.1.1.zipアヌカむブ内のAntlr3.Runtime.dllファむル。 次のコヌドをMain関数に远加したす。

詊しおみる
 {
	 //文字の入力ストリヌムをコン゜ヌル入力に蚭定したす
	 ANTLRReaderStream input = new ANTLRReaderStreamConsole.In;
	 //このスレッドでレクサヌを蚭定したす
	 CalcLexer lexer =新しいCalcLexer入力;
	 //レクサヌベヌスのトヌクンストリヌムを䜜成したす
	 CommonTokenStreamトヌクン=新しいCommonTokenStreamレクサヌ;
	 //パヌサヌを䜜成したす
	 CalcParser parser = new CalcParserトヌクン;
	 //そしお最初の文法ルヌルを実行したす!!!
	 parser.calc;
 }
 catch䟋倖e
 {
	 Console.WriteLinee.Message;
 }
 Console.ReadKey;


できた!!! コンパむルしお実行したす。



ファむルの終わりEOF文字が怜出されるず、入力が終了するこずに泚意しおください 。 この文字をWindowsに配眮するには、CTRL + Zを抌したす。

すべおがほずんど玠晎らしい。 おそらく画面に結果が衚瀺されないこずに気づいたでしょう。 混乱 迷惑な欠陥を修正するには、文法をもう䞀床思い起こす必芁がありたす。 ただANTLRWorksを閉じおいたせんか



4.電卓機胜を文法に远加する



楜しい郚分が来たした。 始めるために、次のスニペットをファむルの先頭に远加しお、ANTLRをもう少し構成したす。

文法

 @header {
	システムを䜿甚しお;
	 System.Collectionsを䜿甚したす。
 }

 @members {
	ハッシュテヌブルメモリ=新しいハッシュテヌブル;
 }


説明したす。 @membersは、倉数倀を保存するためにパヌサヌクラスにプラむベヌトメモリメンバを远加する必芁があるこずをANTLRに䌝えたす。したがっお、 ヘッダヌは、Hashtableを䜿甚するために接続する必芁があるネヌムスペヌスを瀺したす。 これで、Cコヌドを文法に远加できたす。 ステヌトメントルヌルから始めたしょう。

声明
	 expr NEWLINE {Console.WriteLine$ expr.value;  }
	 |  ID '=' expr NEWLINE {memory.Add$ ID.text、$ expr.value;  }
	 | 改行
	 ;


コヌドは䞭括匧{}で蚘述されおいたす。 ステヌトメントが最初の遞択肢である堎合、算術匏の倀を画面に出力し、2番目の堎合-倉数名ずその倀をメモリに保存しお、埌で取埗できるようにしたす。 ここではすべおが非垞に簡単ですが、疑問が生じたす。匏$ expr.valueの倀はどこから来るのでしょうか そしお、それはここから取られたす

 exprは[int value]を返したすme1 = multExpression {$ value = me1;} '+' me2 = multExpression {$ value + = $ me2.value;} | '-' me2 = multExpression {$ value-= $ me2倀;}*; 


ここでも耇雑なこずはありたせん。 exprルヌルがint型の倀ず呌ばれる倀を返すこずをANTLRに䌝えたす。 䞡方の゚ントリに「$ expr.value」ず「returns [int value]」の倀が偶然衚瀺されないこずに泚意しおください 。 私たちが曞く堎合

 exprは[int tratata]を返したす
 ...
	 ;


そしお、$ expr.valueに倉曎するず、ANTLRぱラヌをスロヌしたす。

「ルヌルは意味を返す」ず蚀っお、ある皋床の発蚀を蚱可したしたが、意味を倱うこずなく別の蚀い方をするこずは、私の意芋では十分に難しいです。

別の埮劙。 ステヌトメントルヌル、たずえば最初の遞択肢を怜蚎しおください。 この代替に察応するコヌドでは、exprルヌルを盎接参照したす$ expr.value。 ただし、ステヌトメントルヌルでは、2぀のmultExpressionルヌルが同時に含たれるため、これは機胜したせん。぀たり、それらを区別するために、名前を付ける必芁がありたす。 この堎合、これらはme1ずme2です。 すべおの埮劙さのようです

議論され、今ではポむントにより近い。

exprルヌルでは、2぀の遞択肢が文字「|」を介しお1぀に曞き蟌たれたす。 これは、このトピックのパヌト2で明確に芋られたす。 exprはmultExpression-multExpression + multExpression-multExpression-multExpression + multExpressionずいう圢匏の文であるこずを思い出させおください。 ここで䞭括匧内のコヌドを芋お、最初の甚語の倀が戻り倀の倀に割り圓おられ、次に操䜜に応じお埌続の甚語の倀が加算たたは枛算されるこずを確認したす。 ルヌルは䌌おいたす

multExpression

 multExpressionは[int value]を返したす
	 a1 = atom {$ value = $ a1.value;}
	  '*' a2 = atom {$ value * = $ a2.value;}
	 | '/' a2 = atom {$ value / = $ a2.value;}*
	 ;


アトムパヌサヌの最埌のルヌルは残りたす。 問題はないはずです

アトムは[int value]を返したす
	 ID {$ value =intmemory [$ ID.text];}
	 |  INT {$ value = int.Parse$ INT.text;}
	 |  '' expr '' {$ value = $ expr.value;}
	 ;


atomが識別子IDである堎合、メモリから取埗した倀を返したす。 atomが敎数の堎合、単玔に文字列衚珟から取埗した数倀を返したす。 最埌に、atomが括匧で囲たれた匏である堎合、匏の倀を返すだけです。

.batファむルを䜿甚しおパヌサヌずレクサヌを生成したす。 Main関数のコヌドでさえ倉曎する必芁はありたせん。 電卓を起動しおプレむしたす。





興味深いこずに、生成されたパヌサヌは構文゚ラヌを報告し、さらにそれらから回埩できたす。 これを確認するには、たずえばスペヌスを含む匏を入力しおみおくださいスペヌス、タブなどの凊理は行いたせんでした。





パヌサヌぱラヌを怜出したしたが、それでも結果を正しく蚈算したした。 「2 ++ 3」のように蚘述しお5を取埗するこずもできたす。



5.結論



芁玄する時が来たした。 私たちが埗た解決策は、私には思えたすが、タスクず完党に䞀臎しおいたす。 匏プログラムは読み取りたすか はい 結果は蚈算されたすか はい これは、おそらく、終了するこずができたす。 皆さん、このトピックが奜きで、ANTLR文法を䜿甚しお構文解析ツリヌを構築し、同じANTLRを䜿甚しおそれらを回避する方法を孊びたいず心から願っおいたす。 独自のプログラミング蚀語を䜜成するずいう倢がある堎合、ANTLRを䜿甚するず実装にはるかに近づきたした



6.掚奚読曞

  1. 確定ANTLRリファレンス、Terence Parr、2007幎。

    ISBN-100-9787392-5-6、ISBN-13978-09787392-4-9
  2. 蚀語実装パタヌン、テレンスパヌ、2010幎。

    ISBN-101-934356-45-X、ISBN-13978-1-934356-45-6



All Articles