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