web-dev-qa-db-ja.com

どのプログラミング言語がコンテキストフリーですか?

または、もう少し正確に言えば、どのプログラミング言語が文脈自由文法によって定義されているのでしょうか?

私が集めたものから、マクロやテンプレートのようなもののために、C++はコンテキストフリーではありません。私の直感は、関数型言語はコンテキストフリーかもしれないと私に言っていますが、それを裏付けるハードデータはありません。

簡潔な例の追加担当者:-)

57
n3rd

構文的に正しいプログラムのセットは、ほとんどすべての言語でコンテキストフリーです。

コンパイルするプログラムのセットは、ほとんどすべての言語でコンテキストフリーではありません。たとえば、すべてのコンパイルCプログラムのセットにコンテキストがなかった場合、通常の言語(正規表現とも呼ばれる)と交差することにより、一致するすべてのコンパイルCプログラムのセット

^int main\(void\) { int a+; a+ = a+; return 0; }$

は文脈自由ですが、これはa ^ kba ^ kba ^ kという言語に明らかに同型です。これは、文脈自由であることはよく知られていますnotです。

34
Dave

どのプログラミング言語がコンテキストフリーですか? [...]

私の腸は、関数型言語はコンテキストフリーかもしれないと私に言っています[...]

短いバージョン:Wordの意味で文脈自由な現実世界のプログラミング言語はほとんどありません。言語が文脈自由であるかどうかは、それが機能的であることとは何の関係もありません。言語のルールと機能を解析するのがいかに複雑かという問題です。

命令型言語のCFGです Brainfuck

Program → Instr Program | ε
Instr → '+' | '-' | '>' | '<' | ',' | '.' | '[' Program ']'

そして、これはfunctionalSKI Combinator calculus のCFGです:

Program → E
E → 'S' E E E
E → 'K' E E
E → 'I'
E → '(' E ')'

これらのCFGは非常に単純であるため、2つの言語のallすべての有効なプログラムを認識します。


長いバージョン:通常、文脈自由文法(CFG)は、大まかに言語の構文。 構文的に正しいプログラムとコンパイルするプログラムを区別する必要があります。最も一般的には、コンパイラーは言語分析を構文分析に分割して、コードの一般的な構造を構築および検証し、意味分析プログラムの意味を検証します。

「コンテキストフリー言語」が「...すべてのプログラムがコンパイルされる」を意味する場合、答えはほとんどありません。この法則に適合する言語には、変数の存在、空白の区別、型システム、その他コンテキストのようなルールや複雑な機能はほとんどありません。別のものに頼りました。

一方、「文脈自由言語」が「...のみを意味し、すべてのプログラムが構文解析に合格する」の場合、答えは構文だけでどれほど複雑かという問題です。 CFGだけでは説明が難しい、または不可能である多くの構文機能があります。これらのいくつかは、カウンター、ルックアップテーブルなどを追跡するためにパーサーに追加の状態を追加することによって克服されます。

CFGで表現できない構文機能の例:

  • PythonやHaskellなどのインデントおよび空白に敏感な言語。任意にネストされたインデントレベルを追跡することは、基本的に状況依存であり、インデントレベルに個別のカウンターが必要です。両方に使用されるスペースの数各レベルとそのレベルの数。

    一定量のスペースを使用して一定レベルのインデントのみを許可することは、インデントの各レベルの文法を複製することで機能しますが、実際にはこれは不便です。

  • C Typedef解析の問題 は、何かが通常の識別子であるか既存の型のtypedefエイリアスであるかを文法だけでは認識できないため、字句解析中にCプログラムがあいまいであることを示しています。

    例は次のとおりです。

      typedef int my_int;
      my_int x;
    

    セミコロンで、型環境をmy_intのエントリで更新する必要があります。しかし、レクサーが既にmy_intを監視している場合は、型名ではなく識別子としてそれを字句解析します。

  • LISP、C++、Template Haskell、Nimなどのマクロベースおよびテンプレートベースの言語。構文解析中に構文が変化するため、1つの解決策は、パーサーを自己変更プログラムにすることです。 「 C++は文脈自由か文脈依存か? 」も参照

  • 多くの場合、演算子の優先順位と結合性は、が可能であっても、CFGで直接表現されません。たとえば、^がxよりも強く結合し、×が+よりも強く結合する小さな式文法のCFGは、次のようになります。

    E → E ^ E
    E → E × E
    E → E + E
    E → (E)
    E → num
    

    このCFGは あいまい ですが、多くの場合 優先順位/関連性テーブル が付随しています。つまり、^は最も強く結合し、×は+よりも強く結合し、^は右結合であり、×および+は左結合です。

    優先順位と結合性は、あいまいさをなくし、演算子が正しく動作する構文ツリーのみを生成するように、機械的な方法でCFGにエンコードできます。上記の文法の例:

    E₀ → EA E₁
    EA → E₁ + EA
    EA → ε
    E₁ → EM E₂
    EM → E₂ × EM
    EM → ε
    E₂ → E₃ EP
    EP → ^ E₃ EP
    E₃ → num
    E₃ → (E₀)
    

    しかし、あいまいなCFG + precedence /連想テーブルは一般的です。これらは読みやすく、さまざまなタイプの LRパーサー ジェネレーターライブラリが shift/reduce競合を排除 によってより効率的なパーサーを生成できるためです。より大きなサイズの明確な変換された文法を処理する代わりに。

理論的には、すべての有限の文字列セットは通常の言語であるため、制限されたサイズのすべての法的プログラムは通常のものです。通常の言語は文脈自由言語のサブセットであるため、制限されたサイズのすべてのプログラムは文脈自由です。議論は続く、

100万行未満のプログラムのみを許可することは、言語にとって許容できる制限であると主張することはできますが、プログラミング言語を通常の言語として説明することは現実的ではありません。
— Torben Morgensenの コンパイラーデザインの基本、ch。2.10.2

CFGについても同様です。サブ質問に少し異なる方法で対処するには、

文脈自由文法によってどのプログラミング言語が定義されている

ほとんどの現実世界のプログラミング言語は、その実装によって定義されており、現実世界のプログラミング言語のほとんどのパーサーは、手書きで作成されるか、コンテキストフリーの構文解析を拡張するパーサージェネレーターを使用します。残念ながら、お気に入りの言語の正確なCFGを見つけることはそれほど一般的ではありません。その場合、通常は Backus-Naur形式 (BNF)、または純粋にコンテキストフリーではない可能性が高いパーサー仕様です。

一般的な文法仕様の例:

41
Simon Shine

質問の理解の仕方によって、答えは変わります。しかし、IMNSHO、正しい答えは、現代のすべてのプログラミング言語は実際には文脈依存であるということです。たとえば、構文的に正しいCプログラムのみを受け入れる文脈自由文法はありません。 Cのyacc/bisonコンテキストフリー文法を指す人は、ポイントを逃しています。

8
starflyer

文脈自由でない文法の最も劇的な例に進むために、Perlの文法は、私が理解しているように、 turing-complete です。

3

あなたの質問を理解したら、文脈自由文法(cfg)で記述できるプログラミング言語を探しているので、cfgはすべての有効なプログラムと有効なプログラムのみを生成します。

したがって、現代のプログラミング言語のほとんど(すべてではないにしても)にはコンテキストがありません。たとえば、ユーザー定義型(現代の言語では非常に一般的)を取得すると、自動的に状況依存になります。

構文の検証とプログラムのセマンティックの正当性の検証には違いがあります。構文のチェックはコンテキストに依存しませんが、セマンティックの正当性のチェックは(ほとんどの言語で)行われません。

ただし、これはそのような言語が存在できないことを意味するものではありません。たとえば、型なし lambda calculus は、文脈自由文法を使用して記述でき、もちろんチューリング完全です。

3
Ginandi

VHDLは状況依存です。

VHDLは中途半端に状況依存型です。プロセス内のこのステートメントについて考えてみます。

jinx := foo(1);

まあ、プロセスのスコープで定義されたオブジェクト(およびその包含スコープ)に応じて、次のいずれかになります。

  • 関数呼び出し
  • 配列のインデックス
  • パラメーターなしの関数呼び出しによって返された配列のインデックス付け

これを正しく解析するには、パーサーが階層的なシンボルテーブル(スコープを囲む)を運ぶ必要があり、現在のファイルでは不十分です。 fooは、パッケージで定義された関数にすることができます。したがって、パーサーはまず、解析するファイルによってインポートされたパッケージを分析し、それらに定義されているシンボルを把握する必要があります。

これは単なる例です。 VHDLタイプ/サブタイプシステムは、同様に状況依存の混乱であり、解析が非常に困難です。

(Eli Bendersky、 「VHDLの解析は[非常に]難しい」 、2009)

2
Graham Gimbert

最近のプログラミング言語のほとんどは、文脈自由言語ではありません。証明として、CFLのルートを掘り下げると、対応するマシンPDAは{ww | w is a string}のような文字列マッチングを処理できません。したがって、ほとんどのプログラミング言語ではこれが必要です。

例:

int fa; // w
fa=1; // ww as parser treat it like this
2
P.R.

Haskell および [〜#〜] ml [〜#〜] はコンテキストフリーをサポートしていると思います。 Haskellについては link を参照してください。

1
RoboAlex