ミニ言語の組み込みをサポートするプログラミング言語は非常に多くあります。PHP は HTML に埋め込まれています。XML は JavaScript 内に埋め込むことができます。Linq は C# に組み込むことができます。Perl には正規表現を埋め込むことができます。
// JavaScript example
var a = <node><child/></node>
考えてみれば、ほとんどのプログラミング言語はさまざまなミニ言語としてモデル化できます。たとえば、Java は、少なくとも 4 つの異なるミニ言語に分割できます。
- 型宣言言語 (パッケージ ディレクティブ、インポート ディレクティブ、クラス宣言)
- メンバー宣言言語 (アクセス修飾子、メソッド宣言、メンバー変数)
- ステートメント言語 (制御フロー、順次実行)
- 式言語 (リテラル、代入、比較、算術)
これらの 4 つの概念言語を 4 つの異なる文法として実装できれば、複雑なパーサーやコンパイラの実装で通常見られるスパゲティ現象の多くを確実に減らすことができます。
私は以前にさまざまな種類の言語のパーサーを実装しました (ANTLR、JavaCC、およびカスタム再帰降下パーサーを使用)。言語が非常に大きく複雑になると、通常、1 つの huuuuuuge 文法になってしまい、パーサーの実装は次のようになります。本当に醜い、本当に速い。
理想的には、これらの言語のいずれかのパーサーを作成するときは、構成可能なパーサーのコレクションとして実装し、それらの間で制御をやり取りするのがよいでしょう。
注意が必要なことは、多くの場合、含まれる言語 (Perl など) が、含まれる言語 (正規表現など) の独自のターミナル センチネルを定義することです。良い例を次に示します。
my $result ~= m|abc.*xyz|i;
このコードでは、メインの perl コードが非標準の終端 "|" を定義しています。正規表現用。正規表現パーサーは、親パーサーに相談しないと式の終端を見つける方法がわからないため、正規表現パーサーを perl パーサーとは完全に異なるものとして実装するのは非常に困難です。
または、Linq 式を含めることを許可する言語があったとしますが、(C# のように) セミコロンで終了する代わりに、Linq 式を角かっこ内に表示することを義務付けたいと思いました。
var linq_expression = [from n in numbers where n < 5 select n]
親言語の文法内で Linq 文法を定義した場合、構文の先読みを使用して括弧の囲みを見つける「LinqExpression」の明確な生成を簡単に作成できます。しかし、親の文法は、Linq 仕様全体を吸収する必要があります。そして、それはドラッグです。一方、別の子 Linq パーサーは、外部のトークン型の先読みを実装する必要があるため、停止する場所を見つけるのに非常に苦労します。
そして、Linq パーサーは親パーサーとはまったく異なる一連のトークン化ルールを定義するため、個別の字句解析/解析フェーズを使用することはほぼ除外されます。一度に 1 つのトークンをスキャンしている場合、親言語の字句解析器に制御を戻すタイミングをどのように知るのでしょうか?
皆さんはどう思いますか?より大きな親言語内にミニ言語を含めるために、明確で分離された構成可能な言語文法を実装するために、今日利用できる最良の手法は何ですか?