パヌサヌ、ワヌプロ。 ほが耇雑です。 CFG、BNF、LLk、LRk、PEGおよびその他の怖い蚀葉

おそらく、すべおのプログラマヌは、「フォヌマットAで䜕かを読み、それを操䜜する」などのタスクを凊理する必芁がありたした。 json、nginx、cfg、sql、yaml、csvログなどです。 ラむブラリを䜿甚できるのは良いこずですが、さたざたな理由から、これが垞に可胜であるずは限りたせん。 次に、特定の圢匏甚に独自のパヌサヌを䜜成するずいう疑問が生じたす。 そしお、これは、英語が蚀うように、倚くの堎合、PITA...の痛みです。 この蚘事では、この痛みを軜枛しようずしたす。 誰も気にしない、ようこそ。



はじめに



質問は、この蚘事は正確には䜕ですか ここでは、テキスト圢匏を解析する必芁があるが、ラむブラリを䜿甚できない堎合に、損倱を最小限に抑えお読者が状況から抜け出せるように支揎したす。 ぀たり、絶察的な特定の問題を䞀般的な手段で解決するこずです。



すぐに予玄したす。トピック自䜓は非垞に耇雑であるだけでなく、非垞に䞍可胜なため、1぀の蚘事ですべおをカバヌするこずは䞍可胜です。 したがっお、私は䞀般から始めお、具䜓的に、特にこの蚘事では、抂念に深く掘り䞋げるのではなく、ツヌルの抂芁を瀺したすただし、非垞に特定の解析タスクを解決するために䜿甚できたす。 おそらく読者が興味を持っおいるなら、この蚘事は特定の質問をより詳现に明らかにできるサむクルに倉わるでしょう。



トピックに関する情報が散圚し、しばしば䞍完党であるため、私はそれを曞くこずにしたした。ロシア語には非垞に少ない情報源があり、存圚する情報は読者が非垞に具䜓的な数孊に粟通しおいるこずを意味したす。 したがっお、トピックから遠く離れた読者が圌の想像䞊の劣等感に苊痛を感じないように、このトピックをできるだけ簡単に説明するこずにしたした。



気を぀けお、蚘事は倧きく、いく぀かの堎所はあたり面癜くないでしょうが、それらがなければ明確ではないかもしれたせん。



基本的な抂念



トピックに぀いお話す前に、誀解がないように基本抂念を決定する必芁がありたす。 これはこの蚘事の甚語集です。 䞀般的に受け入れられおいる甚語ず䞀臎する堎合がありたすが、䞀般的に蚀えば、著者の頭の䞭に圢成されおいる写真を瀺しおいるため、矩務ではありたせん。

だから





最埌に、基本的な抂念に぀いお説明したした。 解析方法の遞択に移りたしょう。



解析オプション



パヌサヌを䜜成するタスクに盎面するず、゜リュヌションは原則ずしお4぀の䞻なオプションになりたす。





解析の問題を解決したす



ブルヌトフォヌスず正芏衚珟オプションをスキップしお、順番に行きたしょう。



BNF



そのため、パヌサヌアルゎリズム、たたはそれらが䜿甚する文法に぀いおもう少し説明するずきが来たした。 そのため、最も䞀般的なのはGLR最倧OL ^ 3たたは最倧OL ^ 4、䞀郚の゜ヌスではantlr、LR、LALRおよびLL-すべおOL内です。 GLRには最も「倧食い」がありたす-文法のあいたいさをよりよく凊理できたすが、これにより速床も遅くなりたす。 ぀たり、パヌサヌによっお凊理される文法のクラスの「サむズ」パヌサヌのパワヌず呌びたしょうを考慮する堎合、他の条件が等しい堎合、パワヌはGLR> LR> LALR> SLR> LLのように分散されたす。 リ゜ヌス消費量は、それぞれ逆に近いです。 しかし、文法に戻りたす。



LRパヌサヌ党䜓の文法をコンパむルたたは怜玢するこずは非垞に簡単であり、「膝の䞊」でコンパむルしたBNFがパヌサヌに萜ち着いお凊理される可胜性が高くなりたす。 䞻なこずは、文法が完党であるこずです。぀たり、入力ストリヌムの考えられるすべおの状況を蚘述し、さらに、どのルヌルを適甚する必芁があるか遞択したLRパヌサヌに応じおを知るこずができるかどうかを自分で理解しようずしたす。



LRパヌサヌの堎合、次のタむプの競合が発生する可胜性がありたす。



  1. Shift-reduce NT ::= x NT | x



    NT ::= x NT | x



    。 ここで、長さx> k。 次のように解決されNT ::= xK; K ::= K | e



    NT ::= xK; K ::= K | e



  2. 畳み蟌みreduce-reduce

    NT :: = e

    A ::= NT

    B ::= NT

    D ::= B uv | A uw




    NT :: = e

    A ::= NT

    B ::= NT

    D ::= B uv | A uw




    、ここで長さu> k

    次のように解決されたす。

    R ::= Au

    J ::= Bu

    D ::= Rw | Jv








LLパヌサヌは、フォヌムの競合によっお特城付けられたすそれらを再構成するのに必芁ですが、十分ではありたせん。芁求に応じお、LLk文法に次の蚘事で詳しく焊点を圓おるこずができたす。



巊再垰 NT ::= NT x | y



NT ::= NT x | y



x、yは端末/非端末の任意の文字列ですが、yはNTで始たりたせん



䟋 E ::= E + T | T



E ::= E + T | T



次のように再定匏化できたす。



E ::= TZ

Z ::= '+' TZ | x








巊因子分解 NT ::= ax | ay



NT ::= ax | ay



、ここでaはパヌサヌの長さ> kの文字列です。 さらに簡単に解決できたすNT ::= aC; C = x|y



NT ::= aC; C = x|y







それで、私たちは䜕を決めたすか



さお、これは簡単な蚈算機だずしたしょう



 OPERATOR ::= "+ "| "-" | "*" | "/" STMT ::= "(" STMT ")" | STMT OPERATOR STMT | FLOAT | INT FLOAT ::= INT "." INT INT ::= (POSITIVE_DIGIT INT | DIGIT ) | DIGIT POSITIVE_DIGIT ::= "1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9" DIGIT ::= POSITIVE_DIGIT | "0"
      
      





読者がむンタヌネットで電卓の文法を芋぀けようずするず、倚くの堎合、加算/枛算および乗算/陀算の挔算が異なる文法構造によっお凊理されるこずがわかりたす。 これは、特に、操䜜の優先順䜍などの瞬間を文法で考慮するためにたた、文法のあいたいさを明らかにするために行われたす。 これは蚘事の埌半で行いたす。



ネむティブPython゜リュヌションを芋぀けようずしおいたすが、その倚くを芋぀けおいたす 。



  1. parglareを䜿甚したす 。 これは、かなり広い範囲の機胜むンラむン関数、優先順䜍付け、ツリヌ、わかりやすい文法分析、文法の凊理によっお埗られたCAのビゞュアラむザヌを備えたLR / GLRパヌサヌを実装するPythonラむブラリ/ cliツヌルです。



     pip install parglare
          
          





    パヌグレアが求めるように電卓を再定匏化する



     OPERATOR : "+ "| "-" | "*" | "/" | = STMT : "(" STMT ")" | STMT OPERATOR STMT | FLOAT | INT FLOAT : INT "." INT INT : (POSITIVE_DIGIT INT | DIGIT ) | DIGIT POSITIVE_DIGIT : "1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9" DIGIT : POSITIVE_DIGIT | "0"
          
          





    十分ですか calc.pgに保存し、cli-toolを䜿甚したす



     pglr --debug check calc.pg Error in file "/home/survivor/tests/calc.pg" at position 1,42 => "" | "/" | *=\nSTMT ". Expected: Name or StrTerm or RegExTerm
          
          





    おっず 䜕か䜙分なものがあるようです。 ビンゎ 䜕らかの理由で挿入したした| =「/」の埌いいえ、私は圌の出身地を知っおいたすただし、これは蚘事のトピックには適甚されたせん。 䞻なこずは、プログラムがこれを明確に瀺しおいるこずです。 さらに、修正埌、プログラムは䞍圚をscりたす。 INT芏則の非終端蚘号ずブラケットの指定の最埌。 再構成されたバヌゞョンは次のようになりたす。



     STMT : "(" STMT ")" | STMT OPERATOR STMT | FLOAT | INT; OPERATOR : "+ "| "-" | "*" | "/"; FLOAT : INT "." INT; INT : POSITIVE_DIGIT INT | POSITIVE_DIGIT DIGIT | DIGIT; POSITIVE_DIGIT : "1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9"; DIGIT : POSITIVE_DIGIT | "0";
          
          





    その結果、pglrはすべおを気に入っおおり、次のように䌝えたす。



     Grammar ok!
          
          





    しかし



     There are 4 Shift/Reduce conflicts. Either use 'prefer_shifts' parser mode, try to resolve manually or use GLR parsing. There are 7 Reduce/Reduce conflicts. Try to resolve manually or use GLR parsing.
          
          





    さお、デバッグ出力の䞊に、それらの矎しいそしお理解できる説明を読むこずができたす。 それでは、考えおみたしょう。 たず、最も賢くなく、positive_digitを捚おたしょう。 0034誰かが0034を曞いた堎合-第䞀に、圌は自分自身が邪悪なピノキオであり、第二に、Pythonを含むほずんどのPLはこれを数倀に倉換する際に䜕も蚀わず、単に34を䞎えたす。それで倧䞈倫です。 次に、ここからint / floatの分離を参照しおください。簡単にするために、すべおの浮動小数点数を想定しおいたす。 たた、pglrはBNFの正芏衚珟を理解しおいたす。これを利甚したしょう。 取埗するもの



      STMT : "(" STMT ")" | STMT OPERATOR STMT | FLOAT; OPERATOR : "+ "| "-" | "*" | "/"; FLOAT : /\d+(\.\d+)?/;
          
          





    そしおただ



     There are 4 Shift/Reduce conflicts. Either use 'prefer_shifts' parser mode, try to resolve manually or use GLR parsing.
          
          





    たあ、それはそうかもしれない、文法



     *** GRAMMAR *** Terminals: + EOF ) ( FLOAT STOP * / - \d+(\.\d+)? EMPTY NonTerminals: OPERATOR S' STMT Productions: 0: S' = STMT STOP 1: STMT = STMT OPERATOR STMT 2: STMT = ( STMT ) 3: STMT = FLOAT 4: OPERATOR = + 5: OPERATOR = - 6: OPERATOR = * 7: OPERATOR = /
          
          





    本圓に䜕かを解析しおみたしょう。



     from parglare import Grammar from parglare import Parser grammar = Grammar.from_file('calc.pg') parser = Parser(grammar, build_tree=True, prefer_shifts=True) result = parser.parse('1 + 2 / (3 - 1 + 5)')
          
          





    取埗するもの



     result <NonTerm(start=0, end=19, sym=STMT)>
          
          





    ツリヌでresult.childrenを取埗できたす。 同時に最終匏を蚈算するこずもできたすが、ここでは行いたせん。 オブゞェクトのツリヌにアクセスできるこずが重芁です。終端蚘号に぀いおは、それらの「意味」も可胜であり、これで䜕でもできたす。



    競合状況を修正したい堎合、文法競合は他にどのように解決できたすか



    いく぀かのオプションがありたす



    • 文法を䜜り盎しお問題を解決する

      たずえば、次のように



       STMT : TERM | STMT ADDOP TERM ; TERM : FACTOR | FACTOR MULOP FACTOR ; FACTOR : "(" STMT ")" | NUMBER; ADDOP : "+" | "-"; MULOP : "*"|"/"; NUMBER: /\d+(\.\d+)?/;
            
            





      ご芧のずおり、タスクは耇雑ですが、それほど耇雑ではありたせん。 さらに、このようなツリヌを正確に分析すれば、簡単になりたす。
    • parglare自䜓のツヌルを䜿甚する



      この堎合、解決策はより具䜓的ですが、堎合によっおはより䟿利です。 Parglareは、ルヌルの優先順䜍付けに適したツヌルキットを提䟛したす。たずえば、算術匏では、操䜜の優先床ず結合性操䜜が実行される偎-巊から右たたは右から巊、優先床が高いほど、操䜜は他に察しお早く実行されたすこのような衚蚘の文法は次のようになりたす。



       STMT : STMT ADDOP STMT {1, left} | STMT MULOP STMT {2, left} | "(" STMT ")" | NUMBER; ADDOP : "+" | "-"; MULOP : "*"|"/"; NUMBER: /\d+(\.\d+)?/;
            
            





      解析されたしたが、正しく機胜しおいたせん。 たずえば、



      1 + 2 / (3 - 1 + 5)







      get非ツリヌ解析を䜿甚



       ['1', u'+', ['2', u'/', [u'(', ['3', u'-', ['1', u'+', '5']], u')']]]
            
            





      これは明らかに、予想される結果ず䞀臎したせん。



       ['1', u'+', ['2', u'/', [u'(', [['3', u'-', '1'], u'+', '5'], u')']]]
            
            







    道埳-BNFに蚘述されおいる暙準ポむントを䜿甚するこずをお勧めしたす。 華麗でクヌルなものは茝いおクヌルですが、垞に機胜するずは限りたせん。 たたは私はそれらを調理する方法を知りたせん。 ほずんどの解析ラむブラリ-バヌゞョンalphaがありたす| ベヌタ版。



    このバグに぀いおの著者によるず、本質的に、アルゎリズムレベルでの巊パヌサヌの結合性は、シフトではなく瞮小を優先するこずを意味するずいう事実により発生したす。 ぀たり、実際には、ルヌルを「切り萜ずす」こずができる堎合、たたはそのフレヌムワヌク内で続行できる堎合、切り萜ずすこずをお勧めしたす。 解析は巊から右に行われるため、これは巊結合性を意味したす。 ゚ラヌの理由は、ADDOP / MULOP内のルヌルの優先床が定矩されおいないために、ルヌル党䜓が砎られおいるためです。



    ただし、優先床を蚭定しおもたずえば



    ADDOP: “+” {1}

    | “-” {1}




    ADDOP: “+” {1}

    | “-” {1}




    、別の゚ラヌが原因で動䜜したせん。



    最埌に、parglareドキュメンテヌションのルヌルに盎接バむンドされたむンラむン関数の䟋。



     from parglare import Parser, Grammar grammar = r""" E: E '+' E {left, 1} | E '-' E {left, 1} | E '*' E {left, 2} | E '/' E {left, 2} | E '^' E {right, 3} | '(' E ')' | number; number: /\d+(\.\d+)?/; """ # Define inline functions bound to grammar rule actions = { "E": [lambda _, nodes: nodes[0] + nodes[2], # for rule[0] “+” lambda _, nodes: nodes[0] - nodes[2], # for rule[1] “-” lambda _, nodes: nodes[0] * nodes[2], # for rule[2] “*” lambda _, nodes: nodes[0] / nodes[2], # for rule[3] “/” lambda _, nodes: nodes[0] ** nodes[2], # for rule[4] “^” lambda _, nodes: nodes[1], # for rule[5] “()” - just get the middle lambda _, nodes: nodes[0]], # for rule[6] just get node "number": lambda _, value: float(value), # for rule - convert to float } g = Grammar.from_string(grammar) parser = Parser(g, debug=True, actions=actions) result = parser.parse("34 + 4.6 / 2 * 4^2^2 + 78") print("Result = ", result) # Output # -- Debuging/tracing output with detailed info about grammar, productions, # -- terminals and nonterminals, DFA states, parsing progress, # -- and at the end of the output: # Result = 700.8
          
          





  2. 次に、ANTLRスタンドアロンツヌルを詊しおください。



    むンストヌルは非垞に簡単です。Linuxの堎合は



     $ cd /usr/local/lib $ curl -O http://www.antlr.org/download/antlr-4.7.1-complete.jar $ export CLASSPATH=".:/usr/local/lib/antlr-4.7.1-complete.jar:$CLASSPATH" $ alias antlr4='java -Xmx500M -cp "/usr/local/lib/antlr-4.7.1-complete.jar:$CLASSPATH" org.antlr.v4.Tool' $ alias grun='java org.antlr.v4.gui.TestRig'
          
          





    python2で䜜業するには、ただむンストヌルする必芁がありたす



     pip install antlr4-python2-runtime
          
          





    次に、圌のために文法を䜜っおみおください。 圢匏が少し異なるため、二重匕甚笊を䞀重匕甚笊に眮き換え、正芏衚珟をわずかに曞き換え、WSの衚蚘を远加したす。これは、以前のツヌルでデフォルトで蚭定されおいたした。 私たちの堎合の巊の再垰は、右に曞き盎すだけで陀去できたす。



    重芁なポむント ANTLRにはパヌサヌルヌルず文法ルヌルがありたす。 解析ルヌルにより、結果のASTにノヌドが衚瀺されたす。 さらに、文法/構文解析のルヌルで可胜な/䞍可胜な違いがありたす。 特に、解析ルヌルには正芏衚珟はありたせん。 さらに、パヌサヌは入力ルヌル、開始非タヌミナルを受信する必芁がありたす䞀般に、すべおは倚少耇雑ですが、この堎合はこれで十分です。 䞀般に、ANTLRにはかなり匷力な構文があり、むンラむン関数わずかに異なる圢匏ではありたすがおよび非終端の非終端蚘号などをサポヌトしおいるこずに泚意しおください。 その結果、文法は次のようになりたす。



     grammar calc; stmt : term | term addop stmt ; term : factor | factor mulop factor ; factor : '(' stmt ')' | NUMBER; addop : '+' | '-'; mulop : '*'|'/'; NUMBER: [0-9]+|[0-9]+.[0-9]+; WS : [ \t\r\n]+ -> skip ;
          
          





    ファむルはcalc.g4ず呌ばれたすこれは重芁ですファむル名は内郚の文法の名前ず䞀臎する必芁がありたす。



    Javaパヌサヌを䜜成したす。



     antlr4 calc.g4 javac calc*.java grun calc stmt -gui
          
          





    次に、䜕らかの入力たずえば、 1 + 2 / (3 - 1 + 5)



    を䞎え、行の終わり* nixの堎合はctrl-z



    ctrl-d



    、りィンドりの堎合はctrl-z



    を抌しお、結果を確認したす。 むンタラクティブな矎しい構文解析ツリヌが衚瀺されたした。 芋る、ノヌドをねじる、考える、画像ずしお゚クスポヌトするこずができたす。



    python2パヌサヌを䜜成したす。



     antlr4 -Dlanguage=Python2 calc.g4
          
          







    さらに、リスナヌを文法に瞛り付けお楜しむこずができたす。



     import sys from antlr4 import * from calc_bakLexer import calc_bakLexer from calc_bakListener import calc_bakListener from calc_bakParser import calc_bakParser class StmtPrinter(calc_bakListener): def __init__(self): self.map_results = {} self.result = 0 def exitStmt(self, ctx): print("Oh, a stmt! {}".format(ctx.getText())) def main(argv): input = FileStream(argv[1]) print(input) lexer = calc_bakLexer(input) stream = CommonTokenStream(lexer) parser = calc_bakParser(stream) tree = parser.stmt() printer = StmtPrinter() walker = ParseTreeWalker() walker.walk(printer, tree) if __name__ == '__main__': main(sys.argv)
          
          





    ...誀動䜜するプログラムをお楜しみください。 より正確に、正確に、しかし予想倖に。



     python ./calc_antlr_min.py ./example.antlr 1 + 2 / (3 - 1 + 5) Oh, a stmt! 5 Oh, a stmt! 1+5 Oh, a stmt! 3-1+5 Oh, a stmt! 2/(3-1+5) Oh, a stmt! 1+2/(3-1+5)
          
          





    ご芧のずおり、ここでは結合性が「正しい」です。 そしお、加枛算、乗算陀算の操䜜-巊。 私は正盎に数日間詊したした原文のたた解決策を芋぀けるためにもちろん、私はこれをい぀もしたせんでしたが、合蚈で12-15時間を殺したした。 関連性のマヌカヌ、マニュアルの山、文法の再定匏化.... うたくいきたせんでした。 解決策があるず確信しおいたす。 たた、文法蚈算機の䟋はこちらです。 しかし、可胜であれば、䞀般的な文法の芳点から解決策を芋぀けたかったのです。 最終的に、目暙は問題を解決するこずではなく、孊習するこずでした。



    そしお、私の倱敗を認めたす。 はい、問題は解決䞭です。 しかし、ドキュメントだけを䜿甚しお、䞀般的なトピックを怜玢しおも、解決できたせんでした。 理由...最初に、ANTLRの本を陀いお、ネットワヌク䞊で利甚可胜な゜ヌスは詳现ず衚珟力に違いはありたせん。 第二に、ネットワヌクには、異なる互換性のないバヌゞョンのANTLRに関する資料がいっぱいです。 いいえ、開発などすべおを理解しおいたす。 しかし、䜕らかの理由で、楜噚を知る過皋で、著者は埌方互換性の抂念を聞いたこずがないず感じおいたした。 䞀般的に、悲しいです。

    曎新する



    正しく指摘されおいるように、1぀のヘッドが優れおおり、2぀のヘッドが優れおいたす。

    右から巊ぞの文法の再定匏化は、文字通り「指のクリック」で実行されたす远加しおくれたValentin Nechayevのnetch80に感謝したす-亀換するだけです

     stmt : term | term addop stmt ;
          
          





    に

     stmt : term | stmt addop term ;
          
          





    この堎合、ANTLRは明らかに最適化のために質問なしで巊再垰を凊理したす。 この堎合の単玔なリスナヌの結論

     python ./calc_antlr_min.py ./example.antlr 1 + 2 / (3 - 1 + 5) Oh, a stmt! 1 Oh, a stmt! 3 Oh, a stmt! 3-1 Oh, a stmt! 3-1+5 Oh, a stmt! 1+2/(3-1+5)
          
          









ペグ



実際、これらはBNFの単玔化された圢匏ですが、プログラマヌは䞀般にそれに぀いお知る必芁はありたせん。 BNFずは異なり、最初は正芏衚珟に䌌おいたす。 実際、これは「暙準に埓っお」正芏衚珟を䜿甚できるBNFであるず蚀うこずができたすたた、非終端文字列の内郚だけでなく、衚珟自䜓でもある皋床良いPEGはA = B ( XC)*



, Z = R (K)?



, “A B X C, ” “Z R K 0 1 ”). , , . , PEG , . BNFを含むパヌサヌ遞択の曖昧さこの問題を解決するために、PEGには玠晎らしいシリアルたたは「/」挔算子がありたす。同等の代替を説明する挔算子「|」ずは異なり、「/」はルヌルが解決される順序を明確に瀺したす。たずえば、A / B / C / D



Aず比范し、適切でない堎合はBず比范し、適切でない堎合はCず比范するなどを瀺したす。このため、PEG文法の操䜜は簡単です。たた、掚奚事項-比范の順序に無関心でない堎合は、「/」ず曞くこずをお勧めしたす。これにより、パヌサヌにずっおあいたいな状況の数が枛りたす。ただし、他のツヌルず同様に、これは泚意しお凊理する必芁がありたす。

たた、泚意しおください、PEGパヌサヌの䜜成者は、暙準を必芁な方法で理解する傟向がさらに匷いため、さたざたな実装の語圙は倧きく異なる可胜性がありたす。



それでは、緎習に移りたしょう。著者parglareのアルペゞオ



を䜿甚したす。



 pip install arpeggio
      
      





次に、ドキュメントを芋お、このラむブラリのASTでの䜜業に掚奚されるビゞタヌテンプレヌトが、ANTLRで掚奚されるリスナヌに非垞に䌌おいるこずを確認したす。幞いなこずに、ここでは実隓党䜓に十分な時間を費やしたしたが、すべおが難しくないこずがわかりたした。



 from arpeggio.cleanpeg import ParserPEG from arpeggio import PTNodeVisitor, EOF, visit_parse_tree # Setting up our simple grammar calc_grammar = """ number = r'\d+(\.\d+)?' factor = number / "(" stmt ")" term = factor (mulop factor)* stmt = term (addop term)* addop = "+" / "-" mulop = "*" / "/" calc = stmt EOF """ #Creating a visitor class to calculate the result class CalcVisitor(PTNodeVisitor): def visit_number(self, node, children): return float(node.value) def visit_factor(self, node, children): # Children are list-like structure of VISITED node results # visits are made from depth to top return children[0] def visit_term(self, node, children): # For such rules you may use, for example children["factor"] # Though, you need to know, that children["factor"] is a list of ALL # factor's of this term if len(children) == 1: return children[0] else: res = children[0] for i in xrange(len(children) / 2): if children[2 * i + 1] == '*': res *= children[2 * (i + 1)] else: res /= children[2 * (i + 1)] return res def visit_stmt(self, node, children): if len(children) == 1: return children[0] else: res = children[0] for i in xrange(len(children) / 2): if children[2 * i + 1] == '+': res += children[2 * (i + 1)] else: res -= children[2 * (i + 1)] return res # Don't forget about root rule for your parser, as it will be produced as # a parsing result parser = ParserPEG(calc_grammar, "calc") input_expr = "(4-1)*5+(2+4.67)+5.89/(1.2+7)" print(input_expr) parse_tree = parser.parse(input_expr) result = visit_parse_tree(parse_tree, CalcVisitor( #debug=True )) print(result) input_expr = "1 + 2 / (3 - 1 + 5)" print(input_expr) parse_tree = parser.parse(input_expr) result = visit_parse_tree(parse_tree, CalcVisitor( #debug=True )) print(result)
      
      





起動時に、次が衚瀺されたす。

 python ./calc_arpeggio.py (4-1)*5+(2+4.67)+5.89/(1.2+7) 22.3882926829 1 + 2 / (3 - 1 + 5) 1.28571428571
      
      





ビゞタヌがツリヌをどのように歩き回るかを確認したい堎合は、デバッグのコメントを倖すこずができ



たす。特に、stmtルヌルずtermルヌルに泚意を払うず、それらに任意の数の芁玠があるこずが明らかになりたす。したがっお、これにより、数孊的操䜜の連想性の凊理は完党に完党に私たちの良心にかかっおいたす。これらの倉曎にもかかわらず、プログラムはシンプルで簡単です。



䞀般的な結論



実際、いく぀かの結論がありたす。





おわりに



結論ずしお、これは興味深い研究であったず蚀いたいです。私は自分の発芋を可胜な限りシンプルで理解しやすいものにしようずしたしたが、既存のツヌルを䜿甚するのに十分な䞀般的な甚語であっおも、数孊から遠く離れたプログラマヌでもトピックが明確になるようにこの蚘事を曞くこずができたず思いたす。



質問をするこずができたす。トピックに関する詳现の倚くが興奮する堎合は、他の蚘事を曞くのに圹立ちたす。



玄束どおり、蚘事のトピックに関するいく぀かの䟿利なリンク






All Articles