ソースコードの代わりに構文ツリーを保存しないのはなぜですか?


111

プログラミング言語はたくさんあります。すべての言語が解析され、構文にチェックされてからコードに変換されるため、抽象構文ツリー(AST)が構築されます。

この抽象構文ツリーがあります。ソースコードの代わりに(またはソースコードの隣に)この構文ツリーを格納しないのはなぜですか?

ソースコードの代わりにASTを使用する。チーム内のすべてのプログラマーは、このツリーを(適切な文脈自由文法を使用して)必要な言語にシリアル化し、終了したらASTに解析して戻すことができます。そのため、コーディングスタイルの質問({と}の配置場所、空白、インデントなどの配置場所)に関する議論がなくなります。

このアプローチの長所と短所は何ですか?


37
Lispは通常、抽象的な構文木として書かれています。Algolに似た言語ほど多くは受け入れられませんでした。
デヴィッドソーンリー

2
LISPプログラムは抽象構文ツリーであると言うのはDavidだけだとは信じられません。
WuHoUnited

3
他のポイントに加えて、ASTは最終的なものでもありません。また、コードからASTを作成するのにそれほど時間はかかりません。小規模なVS2010プロジェクトでStyleCopを実行すると、数千行のコードに対して非常に高速(場合によっては1〜2秒)で、多数の異なるASTベースのルールが実行されます。StyleCopを拡張し、カスタムルールを記述することもかなり簡単です。ソースコードをASTに解析することはよく理解されており、比較的簡単な問題だと思います。そもそも良い言語、最適化、そして解析ではなく困難なすべてのライブラリを思いついています。
ジョブ

1
コードを解析した後、別の言語のコードを生成するのはそれほど簡単ではありません。(Prologの暗黙的な統合をCにどのように変換しますか?)。ほとんどの場合、元のプログラムのASTです。
アイラバクスター

3
解析の問題は技術的には十分に理解されていますが、CやC ++を解析するのは簡単ではありません。CやC ++は厄介な厄介な言語だからです。多くのコンパイラは、CまたはC ++をASTにパーサーします。Clang、GCC、...これらはプログラムの保存を目的としておらず、GCCはプログラム分析ツールではなくコンパイラになりたがっています。DMS Software Reengineering Toolkitは、CおよびC ++の多くの方言を解析し、AST、シンボルテーブル、およびさまざまな種類のフロー分析アーティファクトを生成します。このアプローチの大きな長所は、自動化された変更ツールを構築できることです。Semanticdesigns.com/Products/DMS/DMSToolkit.html
イラバクスター

回答:


72

空白とコメント

通常、ASTには空白、行末記号、コメントは含まれません。

意味のあるフォーマット

ほとんどの場合、これは肯定的であり(神聖な戦争の書式設定は不要)、元のコードの書式設定が複数行の文字列リテラルや「コード段落」(空行のあるステートメント)。

コンパイルできないコード

多くのパーサーは欠落している構文に対して非常に回復力がありますが、エラーのあるコードはしばしば非常に奇妙な構文ツリーをもたらします。これはユーザーがファイルをリロードする時点までうまくいきます。IDEでミスをして、突然ファイル全体が「波打つ」ようになったことはありませんか?それが別の言語でどのように再ロードされるか想像してみてください。

ユーザーは解析できないコードをコミットしないかもしれませんが、ローカルに保存する必要があることは確かです。

2つの言語が完全に一致することはありません

他の人が指摘したように、完全な機能パリティを持つ言語はほとんどありません。私が考えることができる最も近いものはVBとC#、またはJavaScriptとCoffeeScriptですが、それでもVBにはC#に相当するものがないXMLリテラルなどの機能があり、JavaScriptからCoffeeScriptへの変換は多くのJavaScriptリテラルをもたらす可能性があります。

個人的体験:

ユーザーがバックグラウンドでJSに変換される「単純な英語」表現を入力することが期待されるため、私が作成するソフトウェアアプリケーションでは、実際にこれを行う必要があります。JSバージョンのみを保存することを検討しましたが、確実にロードおよびアンロードするための受け入れ可能な方法がほとんど見つからなかったため、常にユーザーテキストとJSバージョンの両方を保存することになりました。 「完全に解析されたバージョンかどうか。


9
ASTにはコメントとレイアウトをキャプチャするパーサーがあります。DMS Software Rengineering Toolkitはこれをうまく行います。違法なコードでは苦労します。正確な言語パーサーがあります。
アイラバクスター

2
実際には、JavascriptをCoffeeScript変換するツールがあるので、JavaScriptとCoffeScriptは、Javascriptリテラルなしで相互に翻訳可能であると思います。
ピーターオルソン

興味深いツール、ピーター、私はそれを知りませんでした。
ケビンマコーミック

意味のある書式設定と興味深い個人体験のために+1。-質問には空白は重要ではなく、コメントを残すことができます。とにかくエラーのあるコードは簡単に修正でき、もちろん、質問の「1つの言語ですべてを支配する」部分は到達不能でした。
クレゴックス

43

ソースコードの代わりにこの構文ツリーを保存しないのはなぜですか?チーム内のすべてのプログラマーは、このツリーを任意の言語にシリアル化でき、必要に応じて、ASTに戻って解析します。

実際、それは合理的な考えです。マイクロソフトは、1990年代にほぼ正確にそれを行うための研究プロジェクトを実施しました

いくつかのシナリオが思い浮かびます。

最初の方法はかなり簡単です。あなたが言うように、異なるプログラマーの好みなどに応じて、ASTを異なるビューにレンダリングすることができます。しかし、そのシナリオではASTの保存はやり過ぎです。自分できれいなプリンターを書くだけです。ファイルをエディターにロードしたら、pretty-printerを実行して好みの形式にし、保存したら元の形式に戻します。

二番目はもっと面白いです。抽象構文ツリーを保存できる場合、変更のコード差分はテキストではなく構文になります。コードが移動するリファクタリングは、はるかに理解しやすくなります。マイナス面はもちろん、tree-diffアルゴリズムを書くのは簡単ではなく、多くの場合、言語ごとに行わなければならないことです。テキストdiffは、ほとんどすべての言語で機能します。

3番目は、意図的プログラミングのためにSimonyiが構想したものに似ています。プログラミング言語に共通する基本概念は直列化されたものであり、それらの概念は異なる言語でレンダリングされます。美しいアイデアではありますが、見苦しいのは、言語の細部が十分に異なるため、最小公分母のアプローチではうまくいかないことです。

それで、要するに、それは素敵なアイデアですが、それは比較的小さな利益のための膨大な量の余分な仕事です。だからだれもそれをほとんどしません。


3
実際、言語に依存しない方法でtree-diffを実行できます。ツリーを構築するには、言語固有のパーサーが必要です。多くの言語のASTを比較するSmart Differencerの一連のツールをご覧ください。それらはすべて同じ基礎となる差分エンジンを使用します。 Semanticdesigns.com/Products/SmartDifferencer
Ira Baxter

1
私はいくつかの日のVisual Studioでの私のスタイル-プリティプリントオンロードチームスタイル-プリティプリントオンセーブを見てほしいん...年間、待って...何の運はまだ...
ローマをスターコフ

19

これは.NETのバイトコードとまったく同じであると言えます。Infact redgateのリフレクタープログラムは、バイトコードを一連の.NETプログラミング言語に変換します。

ただし、問題があります。構文は、他の言語では表現できない1つの言語で表現できるものがある限り、言語固有です。これは、C ++が7つのアクセスレベルすべてにアクセスできる唯一の.NET言語である.NETで発生します。

.NET環境以外では、はるかに複雑になります。その後、各言語は独自の関連ライブラリのセットを持つようになります。CとJavaの両方で、非常に異なる方法で類似の問題を解決するのと同じ命令の実行を反映した一般的な構文を反映することはできません。


5
F#で作成されたMSILを逆コンパイルしようとしたことがありますか?
SKロジック

12

私はあなたのアイデアのいくつかが好きですが、あなたは言語を言語に翻訳することがどれほど簡単かをかなり過大評価しています。簡単な場合、言語Xを常にASTに解析してからASTから言語Yに移動できるため、ASTを保存する必要さえありません。

ただし、ある種のAPIを介してASTの一部を公開することについて、コンパイラの仕様をもう少し考えてほしいと思います。アスペクト指向プログラミング、リファクタリング、静的プログラム分析などは、こうしたAPIを使用して実装できます。これらの機能の実装者は、コンパイラライターによって既に実装されている作業の多くをやり直す必要がありません。

プログラムを表現するためのプログラマのデータ構造が、文字列を含むファイルの束としてどれほど頻繁にあるかは奇妙です。


5
Microsoftの「Roslyn」プロジェクトの開発に従って、VBcおよびC#コンパイラをAPIとして公開しましたか?利用可能なプレビューリリースがあります。
Carson63000

11

最も顕著な点は次のとおりです。

  • メリットはありません。あなたは、誰もが自分のペットの言語を使用できることを意味すると言った。しかし、それは真実ではありません。構文ツリー表現を使用すると、構文上の違いだけが排除され、意味的な違いは排除されません。VBとC#、またはJavaとScalaなど、非常に類似した言語である程度機能します。しかし、完全にはありません。

  • 問題があります。あなたは言語の自由を獲得しましたが、ツールの自由を失いました。テキストエディターやIDEでコードを読み取ったり編集したりすることはできなくなります。コードの読み取りと編集の両方について、AST表現を話す特定のツールに依存します。ここでは何も得られません。

    この最後のポイントを説明するために、強力なBASIC方言の独自の実装であるRealBasicを見てください。しばらくの間、この言語はほぼ成功するように見えましたが、独自の非テキスト形式で保存されているため、IDEでしかコードを表示できないという点で、完全にベンダーに依存していました。大きな間違い。


4
潜在的な利点は、「タブとスペース」、「Unixとウィンドウのブレース/インデント」、「メンバーの前または前のm_プレフィックス」などの無限の議論を終わらせることができることです。
ニキエ

1
@nikie確かに、再フォーマットツールastyle(UnniversalIndent など)を使用してこれを行うことができます。難解なバイナリ形式は必要ありません。
コンラッドルドルフ

2
本当の利点は、実際に何が変わったのかをよりよく理解できるようにするdiff / patchツールを持つ可能性です。しかし、それはバージョン管理のためにまったく新しいツールチェーンが必要であることを暗示しているようで、これは深刻な制限です。
ピーターテイラー

1
「メリットがない」と思う場合は、Intentional SoftwareのDomain Workbenchを見たことがないでしょう。
クレイグスタンツ

1
一言で言えば、同じロジックをすべてのテキストベースではなく、異なる表現に投影することができ、プログラマー以外の人もルールにアクセスできます。たとえば、保険数理士などのドメインの専門家は、保険申請の保険数理上の部分を作成できます。DSLに似ていますが、その表現に限定されていません。ただし、これは質問に非常に関連しています。良いデモがあります。
クレイグスタンツ

6

テキストとASTの両方を保存する場合、テキストはすでに1つの言語で存在し、テキストからASTをすばやく再構築できるため、有用なものは何も追加していないと思います。

一方、ASTのみを保存すると、回復できないコメントなどが失われます。


6
また、コメントを構文ツリーの一部にすると(何かの子になることができるコメントノードを使用して)
ラチェットフリーク

私たちのツールはまさにそれを行います。このスレッドの他のコメントを参照してください。
アイラバクスター

4

この考え方は理論的には興味深いものの、異なるプログラミング言語は他の言語では同等のものを持たない異なる構成要素をサポートするため、あまり実用的ではないと考えています。

たとえば、X ++には「while select」ステートメントがありますが、これは多くの追加コード(追加クラス、追加ロジックなど)なしではC#で記述できませんでした。http://msdn.microsoft.com/en-us/library/aa558063.aspx

ここで私が言っているのは、多くの言語には、同じ言語のコードの大きなブロックで、または他の言語にはまったく存在しない要素でさえ翻訳する構文糖があります。ASTアプローチが機能しない理由の例を次に示します。

言語Xには、4つのステートメント(AST、S1、S2、S3、およびS4)で翻訳されたキーワードKがあります。ASTは言語Yに翻訳され、プログラマーはS2を変更します。さて、Xへの翻訳はどうなりますか?コードは、単一のキーワードではなく4つのステートメントとして翻訳されます...

ASTアプローチに対する最後の議論はプラットフォーム関数です。プラットフォームに関数が埋め込まれている場合はどうなりますか?.NETのEnvironment.GetEnvironmentVariableのように。どのように翻訳しますか?


4

この考えに基づいて構築されたシステム:JetBrains MPSがあります。エディターは少し変わっているか、まったく異なるものですが、一般的にはそれほど大きな問題ではありません。他のエディタ、 -最大の問題は、あなたが通常のテキストベースのツールのいずれかを使用することはできませんので、それは、任意のより多くのテキストではないことを、よく、であるgrepsedマージ、および差分ツール、など


2
...しかし、多くのエディター機能をすぐに使用できます。この答えを少し広げてみてください。ソースコードをテキストとして保存しないことの利点をもう少し詳しく説明するに値する非常に興味深い技術です。たとえば、タブ対スペースこの質問に答えたとき
スティーブンジュリス

ASTは、バイナリではなく、人間が読める形式で保存できます。たとえば、Linuxツールを使用して、パラメーターシリアライズ可能オブジェクトとして受け取るコードのすべてのメソッドを置き換えることができますか 書くのは非常に難しいでしょうが、ASTはそれをとても簡単にします。
IAdapter

1
人々は常にこの間違いを犯します。ASTを使用すると、生のテキストだけがある場合よりも簡単になります。しかし、興味深いことには、制御とデータフロー、シンボルテーブル、範囲分析などの追加情報が必要になります。
イラバクスター

@Ira Baxter、もちろんASTの方が簡単です。しかし、既存の インフラストラクチャに統合するのははるかに困難です。
SKロジック

4

実際には、一般に「言語ワークベンチ」と呼ばれるいくつかの製品があり、ASTを保存して、エディターでASTの「投影」を特定の言語に戻します。@ sk-logicが言ったように、JetBrainsのMPSはそのようなシステムの1つです。もう1つは、Intentional SoftwareのIntentional Workbenchです。

ドメイン固有のプロジェクションを作成できるため、言語ワークベンチの可能性は、特にドメイン固有の言語の領域で非常に高いようです。たとえば、回路図として投影される電気に関連するDSLを意図的にデモします。ドメインベースの専門家は、テキストベースのプログラミング言語で記述された回路よりもはるかに簡単かつ正確に議論し、批判します。

実際には、DSLの作業は別として、開発者はおなじみの一般的なプログラミング言語で作業することを好むため、言語ワークベンチの理解が遅れています。テキストエディタまたはプログラミングIDEと直接比較した場合、言語ワークベンチには多くのオーバーヘッドがあり、その利点はそれほど明確ではありません。私が見た言語ワークベンチはどれも、自分のIDEを簡単に拡張できるポイントまでブートストラップされていません。より速く、より速い速度でより良いですか?


「言語ワークベンチ」は、生のASTの保存に必ずしも基づいている必要はありません。同様に、プレーンテキスト構文指向でもかまいません。たとえば、meta-alternative.net / pfront.pdfを参照してください(これは、実際にVisual StudioとEmacsエディターを拡張し、eDSLを実装しています)。
SKロジック

これは興味深い論文です。数週間前にSPLASH / OOPSLAで発表されたSugarJと呼ばれるツールを思い出します(実装ではなく、野心で):uni-marburg.de/fb12/ps/research/sugarj
ラリーオブライエン

興味深いことに、私もそれを試してみます。
SKロジック

3

あなたは私の心を読んでいます。

数年前にコンパイラーコースを受講したとき、ASTを取得し、通常の挿入記法ではなくプレフィックス記法でシリアル化し、括弧を使用してステートメント全体を区切ると、Lispが得られることを発見しました。私は学部の研究でScheme(Lispの方言)について学びましたが、それに対する感謝は決して得られませんでした。そのコースの結果として、私は間違いなくLispとその方言に感謝した。

あなたが提案するものに関する問題:

  1. グラフィカル環境でASTを作成するのは難しい/遅いです。結局のところ、私たちのほとんどは、マウスを動かすよりも速く入力できます。それでも、新たな疑問は「タブレットでプログラムコードをどのように書くのですか?」です。タブレットでの入力は、ハードウェアキーボードを備えたキーボード/ラップトップと比較して、遅く/面倒です。パレットからコンポーネントを大きなタブレットのキャンバスにドラッグアンドドロップしてASTを作成できれば、タブレットでのタッチスクリーンデバイスプログラミングが現実のものになる可能性があります。

  2. これをサポートする既存のツールはほとんどありません。複雑さが増すIDEとますますインテリジェントなエディターを作成するために、何十年もの開発が重ねられています。テキストの再フォーマット、テキストの比較、テキストの検索のためのこれらのツールがすべてあります。ツリー全体で正規表現検索に相当することができるツールはどこにありますか?または2つのツリーの差分?これらはすべてテキストで簡単に行えます。しかし、彼らは言葉を比較することしかできません。単語は異なるが意味の意味は同じであり、それらのdiffツールが問題を起こすように、変数名を変更します。このようなツールは、テキストではなくASTで動作するように開発されているため、セマンティックな意味の比較に近づくことができます。それは良いことです。

  3. プログラムのソースコードをASTに変換することは比較的よく理解されています(コンパイラーとインタープリターがありますよね?)、ASTをプログラムコードに変換することはあまりよく理解されていません。2つの素数を乗算して大きな合成数を得るのは比較的簡単ですが、大きな合成数を素数に戻すことははるかに困難です。ここで、ASTの解析と逆コンパイルを行います。そこで、言語間の違いが問題になります。特定の言語内であっても、ASTを逆コンパイルする方法は複数あります。たとえば、オブジェクトのコレクションを反復処理し、何らかの結果を取得します。forループを使用して、配列を反復処理しますか?それはコンパクトで高速ですが、制限があります。何らかの種類のイテレーターを使用し、コレクションを操作していますか?そのコレクションは可変サイズである可能性があり、速度を犠牲にして柔軟性を追加します。Map / Reduce?より複雑ですが、暗黙的に並列化可能です。そして、それはあなたの好みに応じて、Javaのためだけです。

やがて、開発努力が費やされ、タッチスクリーンとASTを使用して開発することになります。入力する必要が少なくなります。私たちが現在いる場所からの論理的な進歩として、今日コンピューターをどのように使用しているかを見ると、それが#1を解決するでしょう。

すでに木を扱っています。Lispは単にシリアル化されたASTです。XML(およびHTML、拡張)は、単なるシリアル化されたツリーです。検索を行うために、XPathとCSS(それぞれXMLとHTML用)のプロトタイプが既にいくつかあります。CSSスタイルのセレクターと修飾子を作成できるグラフィカルツールが作成されると、#2の一部が解決されます。正規表現をサポートするためにこれらのセレクターを拡張できる場合は、より近いものになります。2つのXMLまたはHTMLドキュメントを比較するための優れたグラフィカルな差分ツールを引き続き探しています。人々がこれらのツールを開発するにつれて、#2は解決できるでしょう。人々はすでにそのようなことに取り組んでいます。彼らはまだそこにいません。

これらのASTをプログラミング言語のテキストに逆コンパイルできる唯一の方法は、目標を追求することです。既存のコードを修正する場合、目標は、修正したコードを可能な限り開始コードに近づけるアルゴリズムによって達成される可能性があります(最小テキスト差分)。コードをゼロから作成する場合、目標は最小で最もタイトなコード(forループの可能性が高い)です。または、可能な限り効率的に並列化するコード(map / reduceまたはCSPに関係する何か)である可能性があります。そのため、同じASTでは、目標の設定方法に基づいて、同じ言語であってもコードが大幅に異なる可能性があります。このようなシステムを開発すると、3番目の問題が解決します。計算が複雑になります。つまり、おそらく何らかのクライアント/サーバー配置が必要になるでしょう。


1

書式設定スタイルに関する議論を排除することを意図している場合、おそらくあなたが望むのは、ソースファイルを読み込み、表示および編集のために個人的な好みに合わせてフォーマットしますが、保存するときに選択したスタイルに再フォーマットするエディタです使用します。

Emacsのようなエディターを使用すれば、非常に簡単です。ファイル全体のフォーマットスタイルの変更は、3つのコマンドジョブです。

また、フックを作成して、ロード時にファイルを独自のスタイルに自動的に変換し、保存時にファイルをチームスタイルに変換できる必要があります。


1
その後、セマンティックdiffとマージ(つまり、ASTレベル)が必要になります。
SKロジック

いいえ、エディターはソースを保存するためにチームスタイルに再フォーマットします。したがって、1つのソースタイプを同じタイプと比較することになります。
グスタフバートラム

良い点は、単一の正規表現は、すべての問題を解決する
SK-ロジック

1
いいえ、2つのファイルを識別のために区画する問題を解決するだけです。ファイル間の違いを確認したい場合は、構造を理解するものが理想的です。私は私のemacsが大好きですが、構造を理解していません。
アイラバクスター

Emacsは素晴らしいですが、diffに使用することはありません。チェックイン前にソースツリーを比較するには、常にmeldを使用します。実際にSVNとgitを理解します。Windowsでは、WinMergeをおそらくカメと組み合わせて使用します。
グスタフバートラム

1

ソースコードではなく、ASTの読み取りと変更は困難です。

ただし、一部のコンパイラ関連ツールでは、ASTを使用できます。Javaバイトコードと.NET中間コードは、ASTと同様に機能します。


1
テキストを使用するよりも、機械ツールを使用してASTを確実に変更する方が簡単です。パターン指向の変更でこれを行うことができます。参照してくださいsemanticdesigns.com/Products/DMS/ProgramTransformation.html
アイラバクスター

2
すぐにこれをLISPersに伝えてください...
hugomg

@アイラバクスター。私は実際に、ASTで直接動作するカスタムビジュアルツールで作業していますが、開発者はビジュアルではなくテキストを使用しなければならない場合があります。一部のASTは、テキストでは短いプログラミング言語として表されます。
umlcat

@ umlcat、AST用の視覚ツールに関するあなたの仕事について詳しく教えてください。
ダニエルアルブスチャット

@Daniel Albuschat私はペットプログラミング言語プロジェクトに取り組んでいますが、パーサーは実装が難しいので、とりあえずスキップし、AST(ツリービューコントロール付きフォーム)を表示し、式を直接追加するツールを作成しました。反対に、ASTからコードを生成できます。
umlcat

0

それはいいアイデアです。ただし、各言語のASTは他のすべてのASTとは異なります。

私が知っている唯一の例外はVB.NETとC#であり、Microsoftはそれらが「異なる構文を持つまったく同じ言語」であると主張しています。他の.NET言語(IronPython、F#など)もASTレベルで異なります。

JVM言語でも同じことであり、それらはすべて同じバイトコードを対象としていますが、言語構成は異なり、異なる言語と異なるASTになります。

CoffeScriptやXtendのような「薄層」言語でさえ、基礎となる言語(それぞれJavaScriptとJava)の理論の多くを共有しています。しかし、ASTレベルで保持される(または保持されるべきである)より高いレベルの概念を紹介します。

XtendをJava ASTから再構築できる場合、既存のJavaコードからより高いレベルの抽象化を魔法のように作成するJava-to-Xtendの「アンコンパイラ」として定義されていると思いますか?


1
C#コンパイラとVBコンパイラの両方に精通している誰かとして、それらは確かに類似しているが、十分に異なる重要な詳細が十分にあるため、それらを「異なる構文を持つ同じ言語」として扱うのは実用的ではないことを伝えることができます。Roslynプロジェクトでそれを検討しました。両方の言語を同等の機能でコンパイルできる単一のコンパイラを構築し、多くの議論の末、2つの言語用の2つのコンパイラを使用することにしました。
エリックリッパー

@EricLippert:それは残念です。いずれかの言語の学習を計画したことはありませんが、それは素晴らしい例外のように聞こえました。htatはlisp-like-Dylanとalgol-like-Dylanを唯一の「異なる構文を持つ同じ言語」の例として残していると思います。
ハビエル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.