しばらく前に、コンテキストを分析して意図を推測することで構文エラーを修正しようとするコンパイラーがあったことを聞きました。
そのようなコンパイラは本当に存在しますか?明らかに実用的な価値はほとんどありませんが、遊んだり、学んだりすることは非常に興味深いでしょう。
しばらく前に、コンテキストを分析して意図を推測することで構文エラーを修正しようとするコンパイラーがあったことを聞きました。
そのようなコンパイラは本当に存在しますか?明らかに実用的な価値はほとんどありませんが、遊んだり、学んだりすることは非常に興味深いでしょう。
回答:
ある意味では、コンパイルの行為をされ、特定の構文が行うことを意図しているものを推測し、コンパイラはそれを把握することができないときに、したがって、構文エラーがあります。より多くの「推測」を追加して、コンパイラにさらに物事を推測させ、構文をより柔軟にすることができますが、特定のルールセットによって推測する必要があります。そして、それらのルールは言語の一部となり、エラーではなくなりました。
だから、いいえ、そのようなコンパイラはありません、本当に、質問が意味をなさないので。いくつかのルールに従って構文エラーが何を意味するかを推測することは、単に構文の一部になります。
その意味で、これを行うコンパイラーの良い例があります:任意のCコンパイラー。多くの場合、彼らはあるべきではない何かの警告を出力し、それからあなたがXを意味すると仮定して、続けます。これは実際には不明瞭なコードの「推測」であり(ほとんどの場合それ自体は構文ではありませんが)、エラーでコンパイルを停止し、エラーと見なすこともできます。
本当に危険ですね。コンパイラーが意図を推測しようと試み、間違っていると推測し、コードを修正した後、ユーザーに通知しない(または、皆さんと同じように無視するという警告を表示する)場合、コードを実行しようとしています深刻なダメージを与えます。
このようなコンパイラは、おそらく非常に意図的に作成されていないものです。
最近のプログラミング言語のIDEには通常、なんらかの方法でバックグラウンドで実行されているコンパイラがあり、構文の色分け、IntelliSense、エラーなどの分析サービスを提供できます。明らかに、そのようなコンパイラーは、深く壊れたコードを理解できる必要があります。ほとんどの場合、編集時にコードは正しくありません。しかし、それを理解する必要があります。
ただし、通常、エラー回復機能は編集中にのみ使用されます。「メインライン」シナリオで実際のコンパイルを許可することはあまり意味がありません。
興味深いことに、この機能をJScript.NETコンパイラに組み込みました。基本的に、IDEがエラーから回復した場合、エラーが発生してもコンパイラを続行できるモードにコンパイラを配置することができます。あなたは入力できVisual Basicのもう一方の端を出て、内のコードをそれにJScript.NETコンパイラを実行し、作業プログラムの合理的なチャンスがあります!
これは面白いデモですが、多くの理由で「メインライン」シナリオにはあまり良い機能ではないことがわかりました。完全な説明は非常に長くなります。簡単な説明は、予期せず偶然に動作するプログラムを作成し、複数のコンパイラーまたは同じコンパイラーの複数のバージョンで同じコードを実行するのを難しくするということです。この機能が追加する大きな費用は、小さな利点では正当化されません。
過去にこの機能をPMしたPeter Torrが、2003年のこのブログ投稿でこの機能について簡単に説明しています。
JScript .NETエンジンのAPIをホストしているスクリプトを介してこの機能を公開していますが、これを使用した実際の顧客は知りません。
コンパイラーが誤った構文を修正できる場合、その構文は言語で文書化されるべきだと思います。
構文エラーの理由は、パーサーがプログラムから抽象構文ツリーを作成できなかったためです。これは、トークンが適切でない場合に発生します。そのトークンがどこにあるか、それを削除する必要があるか、またはエラーを修正するために他のトークンを追加する必要がある場合、プログラマーの意図を推測できる何らかのコンピューターが必要になります。マシンはどのように推測できますか:
int x = 5 6;
はずだった:
int x = 5 + 6;
これは、同じように簡単に次のいずれかが考えられます56
、5 - 6
、5 & 6
。コンパイラが知る方法はありません。
その技術はまだ存在しません。
まったく同じことではありませんが、これがHTMLが災害に変わった理由です。ブラウザは悪いマークアップを許容し、次に知っていること、ブラウザAはブラウザBと同じようにレンダリングできませんでした(はい、他の理由がありますが、これは10年前のいくつかのルーズネスルールが慣習になる前のトップ数の1つでした) )。
Eric Lippertが推測するように、これらの多くはコンパイラではなくIDEで処理するのが最適です。それは、自動ビットがあなたのために台無しにしようとしているものを見ることができます。
私が現在支配していると思う戦略は、コンパイラを緩めるのではなく、継続的な言語の改良です。それが本当にコンパイラが自動的に理解できるものである場合、その周りに明確に定義された言語構造を導入します。
頭に浮かぶ直接的な例は、C#の自動プロパティです(類似したものがある唯一の言語ではありません):アプリのゲッター/セッターの大部分は実際にはフィールドの単なるラッパーであるため、開発者が意図し、コンパイラに残りを注入させます。
それから私は考えさせられます:ほとんどのCスタイル言語はすでにある程度これをしています。自動的に把握できるものについては、構文を改良してください。
if (true == x)
{
dothis();
}
else
{
dothat();
}
に減らすことができます:
if (true == x)
dothis();
else
dothat();
結局、私はこれに帰着すると思います:傾向は、コンパイラを「スマート」または「ルーサー」にしないことです。それは、よりスマートまたはルーズに作られた言語です。
さらに、古典的な「if」バグのように、「ヘルプ」が多すぎると危険な場合があります。
if (true == x)
if (true == y)
dothis();
else
dothat();
if (x && y) dothis(); else dothat();
少し良くなります。
true
またはに対して比較するたびに死ぬfalse
。
DECとIBMのミニコンピューターとメインフレームシステムで80年代後半と90年代前半にFORTRANとPL / Iをコーディングしていたとき、コンパイラーは定期的に「なんとかエラー。 。 "。当時、これは実行前にコードを送信してから結果を取得するまでに非常に長い間待機していたバッチ処理とパンチカードの(以前よりもずっと前の)日々の遺産でした。そのため、コンパイラーは、最初に遭遇したミスで中止するのではなく、プログラマーを推測し、続行しようとするのは非常に理にかなっています。心に留めておいて、私は「修正」が特に洗練されていることを覚えていません。最終的にインタラクティブなUnixワークステーション(Sun、SGIなど)に移動したとき、
コンパイラの目標は、希望どおりに動作する実行可能ファイルを生成することです。プログラマーが無効なものを書いた場合、たとえコンパイラーが90%の確率で意図を推測できたとしても、コンパイラーが先に進んで実行可能ファイルを生成するよりも、プログラマーにプログラムを修正して意図を明確にするよう要求する方が一般的には良いでしょうバグを隠す可能性が非常に高くなります。
もちろん、言語は一般に、意図を明確に表現するコードが合法になるように設計されるべきであり、意図を明確に表現しないコードは禁止されるべきですが、そうではありません。次のコードを検討してください[JavaまたはC#]
const double oneTenth = 0.1;
const float oneTenthF = 0.1f;
...
float f1 = oneTenth;
double d1 = oneTenthF;
f1
プログラマがf1
含めることができる論理的なものは1つ(float
1/10に最も近い値)しかないため、コンパイラに割り当ての暗黙的な型キャストを追加すると便利です。ただし、コンパイラが不適切なプログラムを受け入れるようにするのではなく、特定のコンテキストで暗黙的にdoubleからfloatへの変換を許可する方が仕様のほうが優れています。反対に、割り当てd1
はプログラマが本当に意図したものである場合とそうでない場合がありますが、それを禁止する言語規則はありません。
最悪の種類の言語規則は、そうでなければ合法的にコンパイルできなかった場合にコンパイラが推論を行うが、推論が意図されていた場合にプログラムが「偶然に」有効になる可能性がある規則です。暗黙のステートメントの終わりを含む多くの状況は、このカテゴリーに分類されます。2つの別個のステートメントを作成しようとするプログラマーがステートメントターミネーターを省略した場合、コンパイラーは通常ステートメントの境界を推測できますが、1つのステートメントを2つとして処理されるはずの何かと見なすことがあります。
構文エラーは特に修正が困難です。権利がない場合)
を考えてみましょう。コードを挿入することでコードを修復できますが、通常、コードを挿入して構文的に正しいプログラムを取得できる場所がたくさんあります。
はるかに簡単なポイントは、スペルミスの識別子です(ただし、これは構文エラーではないことに注意してください)。解決できない識別子とスコープ内のすべての識別子の間の編集距離を計算し、解決できない単語をユーザーが最も意味する可能性の高いものに置き換えることにより、多くの場合、正しいプログラムを見つけ出すことができます。ただし、エラーにフラグを付けて、有効な置換をIDEに提案させる方が良いことがわかります。
Cでは、配列を値で渡すことはできませんが、コンパイラーでは次のように記述できます。
void foo(int array[10]);
その後、次のように静かに書き換えられます。
void foo(int* array);
それはどれほど愚かですか?この特別な規則により、多くのプログラマーが配列とポインターは基本的に同じものであると信じるようになったため、ここではサイレントリライトではなくハードエラーを好むでしょう。ではない。