式ドライバー

数式の分析の範囲は想像するのが難しいことではありません-これらはSQLクエリのパーサーのすべての種類であり、ユーザーが入力した数式のハンドラーです(データベースの同じグラフまたはフィルター)-独自の言語を作成するまで(私は意図的に「プログラミング」という言葉を書きません) to。多くの場合、これらはデータ記述言語などです。



間違っているかもしれませんが、ネットワーク上でPHPの使用可能な表現パーサーを見つけることができませんでした-定期的に私の記事を読んでいる人はすでに慣れているので、私はこのビジネスを自分で実装しました。 車輪を再発明します。 :^)



あなたがここで見つけることができる私の努力の結果。 アーカイブには、ライブラリが機能するために必要なスクリプトとその操作例(sample.php)があります。 ライブラリはスタンドアロンとしてコンパイルされます。



しかし、私は、何が何であるかを理解することは興味深いでしょう。



ご想像のとおり、このライブラリはSCR (ポーランド語の逆表記法、または使い慣れている場合は表記法)に基づいて構築されています。 ただし、SCRは、その説明では式を解析するための基本的な機能のみが提供されているため、目的の条件に調整することは理にかなっています。 したがって、ここにライブラリの作業スキームがあります:



式を解析するためのルールは、CIprクラスから継承されたクラスで説明されています。 文法とクラスの定義は、protected p_buildメソッドで行われます。



まず、文法クラス(または文法 )を定義する必要があります。これに基づいて、パーサーはそれに渡された文字列をトークンに分割します。 各トークンは、トークン(実際の単語)とクラス識別子(このトークンの選択を引き起こしたもの)で構成されます。 クラス識別子は、配列のインデックスです。 文字列または整数にすることができます(わかりやすくするために文字列を使用しました)。 次の種類の文法を特定しました。



grammar_char(文字[、組み合わせ])

このような文法は、文字のセット(これらの文字がリストされている文字列)と、場合によっては文字の組み合わせ(文字列の配列)によって定義されます。

定義は次のようになります: grammar_char( "+-* / ="、array( "+ ="、 "++")) 。 この文法では、単一の文字+、-、*、/、および=と、それらの組み合わせ+ =および++を定義しています。



grammar_list(トークン[、大文字と小文字の区別= true])

このタイプの文法は、事前定義されたトークンのセットによって定義されます。 トークンの定義で大文字と小文字が区別されるかどうかも確認できます(デフォルトではyes)。

次のように使用できます: grammar_list(array( "prefix1"、 "prefix2")、false) 。 この文法は、大文字と小文字を区別しないprefix1およびprefix2トークンを定義します。 セパレーターを考慮せずに、行の先頭にあるリストから1つのトークンを単に検索することが重要です。 トークンprefix1は、式prefix1somethingから選択されます。



grammar_preg(開始[、抽出])

このタイプの文法は、象徴的なものと一緒に、最も役立つと思います。 これらは正規表現の文法です。 それらは、開始の規則性と、おそらく抽出の規則性によって記述されます。 抽出が指定されていない場合、代わりに先頭が使用されます。 正規表現を次に示します。たとえば、 // comment :/^\/\/.*/という形式のコメントを選択します



grammar_quot(引用符文字[、エスケープモード= C_ESCAPE_CHAR])

このタイプの文法は引用文字列です。 シールドモードは次の値を取ることができます。

C_ESCAPE_NONE-文字は文字列でエスケープされません。 (文字列 "10 \" "は10 \として認識されます)。

C_ESCAPE_CHAR-制御文字のみをエスケープできます。 (文字列「10 \ '-\」「10 \'-」として認識されます)。

C_ESCAPE_ALL-エスケープ文字\は、すべての文字の前に配置できます。 (文字列 "1 \ 0 \ '-\" "は10'-として認識されます)。



grammar_brac(括弧文字[、エスケープモード= C_ESCAPE_ALL [、<入れ子を許可する= true>]])

これらはブラケット文法です(つまり、<anfg>型の式を区別します)。 私の個人的な使用は、この文脈では疑わしいようです-それらは他の目的を目的としています(このライブラリはプラットフォームの一部にすぎません)。 ただし、便利な場合は使用できます。 :^)



grammar_user(開始、抽出)

これは、開始および抽出機能によって定義されるカスタム文法です。



構文解析は次のように行われます。入力行はすべての文法によって順番にチェックされ、それらのいずれかの開始点が見つかった場合(通常は行の開始点に一致する)、抽出されます。 解析の結果として、トークンの配列を取得します。



さらに興味深いことに、式コードで可能なコマンドのクラスが定義されています。 次のタイプのコマンドを定義できます。



ipr_lexeme(トークン)

指定されたトークンをトークンタイプのコマンドとして扱うことを指定します。 トークンは、lexeme関数とilexeme関数で定義された完全なトークン、またはトークンのトークンを定義する単なる文字列として機能できます。



ipr_operator(トークン、優先度[、アリティ= 2 [、アソシエティビティ= no(現在は左側と同等、完全に真実ではない)]])

演算子を定義します。

次の関数も使用できます。

ipr_perfix-プレフィックス単項演算子。

ipr_postfix-後置単項演算子;

ipr_binaryは二項演算子です。



ipr_compound(トークンの配列、優先度[、アリティ= 3 [、結合性=右[、自動計算= no]]])

複雑な演算子を定義します。 simpleとの違いは何ですか? 単純な演算子のオペランドは、エンジン自体によって計算されます。たとえば、the ?:演算子は完全に正しいわけではありません。 2番目と3番目のオペランドの1つだけを計算する必要があります。 そのためには、複雑な演算子が必要です。そのためには、最初のオペランドのみが自動的に計算され、後続のすべてのオペランドがユーザーメソッドに渡されて処理されます。 したがって、アリティが少なくとも2の複雑な演算子を定義することは理にかなっています。

次の関数も使用できます。

ipr_access-オブジェクトフィールドにアクセスするためのバイナリ演算子。

ipr_ternaryは三項演算子です。



ipr_brackets(オープニングトークン、クロージングトークン、コンマ[、オペランドなしで使用可能= no [、アリティ=定義なし[、自動計算= yes]]])

括弧を定義します。 括弧は、オペランドなし((a + b)など)またはオペランド付き(arr [10]など)で使用できます。 厳密に定義された数のオペランドを括弧で指定する必要がある場合、括弧のアリティも決定できます。 自動については上記を読むことができます。

次の関数も使用できます。

ipr_default-優先順位を設定するために使用されるブラケットの定義(つまり、デフォルトのブラケット)。



ipr_instruction(トークンオープニングトークン、クロージングトークン、コンマ[、アリティ=未定義[、自動計算= no]])

命令を定義します。 実際には、自動使用が禁止されているブラケットと同じことですが、ブラケットの前のオペランドの代わりに、命令トークンが使用されます。



ipr_end(トークン)

式が終了すると見なされるトークンを定義します。



ipr_until(トークン)

式が終了すると見なされるトークンを定義します。 この場合、トークンは式の残りの先頭に残ります。



ここで、すべてのタイプのコマンドのハンドラーを定義する必要があります。そのうち5つは、オペランド、トークン、演算子、複雑な演算子、括弧、および命令です。 これらは、次の保護されたメソッドです。



p_run_operand(コマンド)

p_run_lexeme(コマンド)

p_run_operator(コマンド、オペランド)

p_run_compound(コマンド、オペランド、オペランド)

p_run_brackets(コマンド、オペランド、オペランド)

p_run_instruction(コマンド、オペランド)



コマンドには、コマンドのタイプ(ハンドラーを定義するため重要ではありません。つまり、オペレーターコマンドがオペランドハンドラーに到達する状況は不可能です)、コマンドのクラス、およびコマンドとして定義されたトークンが含まれます。 オペランドには計算されたオペランドが含まれ、オペランドには、計算された、またはp_runメソッドによる処理を待機しているオペランドが含まれます(コマンドのタイプと「自動計算」フラグによって異なります)。



ここで、式ハンドラーを作成するには、処理される式がコンストラクターに渡されるハンドラークラスのインスタンスを作成する必要があります。 コンストラクターは、式を解析してコンパイルします。 式を開始するには、runメソッドを使用します。



まあ、それがすべてです。 エラー処理と非連想演算子が現在進行中です。近い将来、これらすべてを完了することを望みます。 いつものように、私はこれがもはや当てはまらない人には厳しすぎないようにお願いします-私はそれが有用であると思うかもしれない人のために私の開発を公開します。



ご清聴ありがとうございました! 積極性に関係なく、コメントにコメントやコメントをお寄せいただければ幸いです。 :^)



All Articles