8

ミニ言語の組み込みをサポートするプログラミング言語は非常に多くあります。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 つのトークンをスキャンしている場合、親言語の字句解析器に制御を戻すタイミングをどのように知るのでしょうか?

皆さんはどう思いますか?より大きな親言語内にミニ言語を含めるために、明確で分離された構成可能な言語文法を実装するために、今日利用できる最良の手法は何ですか?

4

6 に答える 6

4

このポッドキャストを聴きたいと思うかもしれません。スキャナーレス構文解析は、さまざまな文法を作成する問題を解決するために「発明」されました(問題は、「ユニバーサル」トークナイザー/スキャナーを作成できないことがすぐにわかることです)。

于 2009-06-04T22:46:00.900 に答える
3

私はこの正確な問題に取り組んでいます。私の考えを共有します:

文法はデバッグが困難です。私はBisonとANTLRでいくつかデバッグしましたが、きれいではありませんでした。ユーザーにDSLを文法としてパーサーに挿入してもらいたい場合は、それが爆発しないようにする方法を見つける必要があります。私のアプローチは、任意のDSLを許可せず、次の2つのルールに従うDSLのみを許可することです。

  • トークンタイプ(識別子、文字列、番号)は、ファイル内のすべてのDSL間で同じです。
  • 不均衡な括弧、中括弧、または括弧は許可されていません

最初の制限の理由は、現代のパーサーが構文解析を字句段階に分割してから、従来の文法規則を適用するためです。幸い、組み込みたいDSLに対応していなくても、作成したいDSLの90%には単一のユニバーサルトークナイザーで十分だと思います。

2番目の制限により、文法を互いにより分離することができます。括弧(中括弧、角かっこ)をグループ化してから、各グループを再帰的に解析することにより、2段階で解析できます。埋め込まれたDSLの文法は、それに含まれている角かっこから逃れることはできません。

ソリューションのもう1つの部分は、マクロを許可することです。たとえば、regex("abc*/[^.]")私には問題ないように見えます。このようにして、マクロ " regex"は、正規表現文法をメイン言語に組み込む代わりに、正規表現を解析できます。確かに、正規表現に異なる区切り文字を使用することはできませんが、私の心の中で一貫性の尺度を得ることができます。

于 2009-06-04T22:14:58.793 に答える
2

SGLR 、スキャナーレスの一般化された LR 解析をご覧ください。参考文献とURLを載せておきます。この解析手法により、解析テーブルの構成が非常に単純になります。特に自衛隊との組み合わせ。

Martin Bravenboer と Eelco Visser。言語ライブラリの構文埋め込みと同化の設計。ソフトウェア エンジニアリングのモデル: MoDELS 2007 でのワークショップとシンポジウム、LNCS のボリューム 5002、2008 年。

MetaBorgMetaBorg の動作

于 2009-06-09T19:31:09.743 に答える
1

解析は問題の 1 つの側面ですが、各ミニ言語に関連するさまざまな実行可能インタープリター間の相互運用は、おそらく解決が非常に難しいと思います。有用であるためには、独立した各構文ブロックが全体的なコンテキストと一貫して機能する必要があります (そうしないと、最終的な動作が予測不能になり、使用できなくなります)。

私は彼らが実際に何をしているのかを理解していませんが、より多くのインスピレーションを探すための非常に興味深い場所はFoNCです. それらは、あらゆる種類の異なる計算エンジンがシームレスに相互作用できる方向に向かっているようです (私は推測しています)。

于 2009-06-04T21:50:32.697 に答える
0

考えてみれば、これが再帰降下解析の仕組みです。各ルールとそれが依存するすべてのルールは、ミニ文法を形成します。それより上のものは問題ではありません。たとえば、ANTLR を使用して Java 文法を記述し、さまざまな「ミニ言語」をすべてファイルのさまざまな部分に分けることができます。

これらの「ミニ言語」が多くのルールを共有することが多いという理由だけで、これはあまり一般的ではありません。ただし、ANTLR のようなツールを使用して、さまざまなファイルから個別の文法を含めることができれば、それは間違いなく素晴らしいことです。これにより、それらを論理的に分離できます。これがおそらく実装されていない理由は、おそらく「表面的な」問題であり、それ自体を解析するのではなく、文法ファイル自体に純粋に関連しているためです。また、コードが短くなることもありません (ただし、従うのが少し簡単になる場合があります)。これで解決できる唯一の技術的な問題は、名前の衝突です。

于 2009-06-04T22:32:25.000 に答える