そのため、レクサー、パーサー、インタープリター、さらにはコンパイルについても少し読んでいます。
私が実装しようとしている言語については、Recrusive Descent Parser に落ち着きました。この言語の元の文法には左再帰があったため、少し書き直さなければなりませんでした。
これは私が持っていた文法の簡略化されたバージョンです(これは標準形式の文法ではありませんが、やや疑似的なものであることに注意してください。ドキュメントで見つけた方法です):
expr:
-----
expr + expr
expr - expr
expr * expr
expr / expr
( expr )
integer
identifier
左再帰を取り除くために、これを次のように変更しました (NOT 演算子の追加に注意してください)。
expr:
-----
expr_term {+ expr}
expr_term {- expr}
expr_term {* expr}
expr_term {/ expr}
expr_term:
----------
! expr_term
( expr )
integer
identifier
そして、次のサブルーチンを使用してトークンを調べます (簡略化された疑似コードっぽい):
public string Expression()
{
string term = ExpressionTerm();
if (term != null)
{
while (PeekToken() == OperatorToken)
{
term += ReadToken() + Expression();
}
}
return term;
}
public string ExpressionTerm()
{
//PeekToken and ReadToken accordingly, otherwise return null
}
これはうまくいきます!呼び出し後の結果Expression
は、与えられた入力と常に等しくなります。
これらのサブルーチンで文字列ではなく AST ノードを作成し、中置エバリュエーターを使用して AST を評価すると (結合性と演算子の優先順位なども考慮されます)、同じ結果が得られるでしょうか? ?
そして、私がそうするなら、実際には解決するのが「非常に単純」であるか、問題がないように見えるのに、「左再帰を修正し、結合性などを念頭に置いて」をカバーするトピックが非常に多いのはなぜですか? それとも、実際に人々が関心を持っている結果の AST の構造なのでしょうか (それが評価されるものではなく)? 誰かが光を当てることができますか、私もそれをすべて間違っているかもしれません、ハハ!