使用可能なJVM言語の作成:概要

公開: 2022-03-11

言語を作成する理由はいくつか考えられますが、そのいくつかはすぐにはわかりません。 既存のツールを可能な限り再利用するJava仮想マシン(JVM)用の言語を作成するためのアプローチと一緒にそれらを提示したいと思います。 このようにして、開発の労力を軽減し、ユーザーに馴染みのあるツールチェーンを提供して、新しいプログラミング言語の採用を容易にします。

使用可能なJVM言語の作成:概要

このシリーズの最初の記事では、JVM用の独自のプログラミング言語の作成に関連する戦略とさまざまなツールの概要を説明します。 今後の記事では、実装の詳細について詳しく説明します。

なぜJVM言語を作成するのですか?

プログラミング言語はすでに無数にあります。 では、なぜわざわざ新しいものを作成するのでしょうか。 それには多くの可能な答えがあります。

まず、さまざまな種類の言語があります。汎用プログラミング言語(GPL)を作成しますか、それともドメイン固有言語を作成しますか? 最初の種類には、JavaやScalaなどの言語が含まれます。これは、多数の問題に対して適切なソリューションを作成することを目的とした言語です。 ドメイン固有言語(DSL)は、代わりに特定の問題のセットを非常にうまく解決することに焦点を当てています。 HTMLまたはラテックスについて考えてみてください。画面に描画したり、Javaでドキュメントを生成したりできますが、面倒です。代わりに、これらのDSLを使用すると、ドキュメントを非常に簡単に作成できますが、特定のドメインに限定されます。

したがって、おそらく、非常に頻繁に作業し、DSLを作成することが理にかなっている一連の問題があります。 同じ種類の問題を何度も解決しながら、生産性を高める言語。

おそらく、代わりにGPLを作成したいのは、たとえば、第一級市民としての関係を表現したり、コンテキストを表現したりするなど、いくつかの新しいアイデアがあったからです。

最後に、楽しくてかっこいい、そしてその過程で多くのことを学ぼうとしているので、新しい言語を作成したいと思うかもしれません。

実際のところ、JVMをターゲットにすると、少ない労力で使用可能な言語を取得できます。これは、次の理由によるものです。

  • バイトコードを生成するだけで、JVMが存在するすべてのプラットフォームでコードを利用できるようになります。
  • JVMに存在するすべてのライブラリとフレームワークを活用できるようになります

したがって、言語開発のコストはJVMで大幅に削減され、JVMの外部では不経済なシナリオで新しい言語を作成することは理にかなっています。

それを使用可能にするために何が必要ですか?

言語を使用するために絶対に必要なツールがいくつかあります。これらのツールには、パーサーとコンパイラー(またはインタープリター)があります。 ただし、これだけでは不十分です。 言語を実際に実際に使用できるようにするには、ツールチェーンの他の多くのコンポーネントを提供する必要があり、場合によっては既存のツールと統合する必要があります。

理想的には、次のことができるようになります。

  • 他の言語からJVM用にコンパイルされたコードへの参照を管理する
  • シンタックスハイライト、エラー識別、オートコンプリートを使用して、お気に入りのIDEでソースファイルを編集します
  • お気に入りのビルドシステム(Maven、gradleなど)を使用してファイルをコンパイルできるようにする必要があります
  • テストを作成し、継続的インテグレーションソリューションの一部として実行できるようにする必要があります

あなたがそれを行うことができれば、あなたの言語を採用することははるかに簡単になります。

では、どうすればそれを達成できますか? 投稿の残りの部分では、これを可能にするために必要なさまざまな部分を調べます。

解析とコンパイル

プログラムでソースファイルを変換するために最初に行う必要があるのは、それらを解析して、コードに含まれる情報の抽象構文木(AST)表現を取得することです。 その時点で、コードを検証する必要があります。構文エラーはありますか? セマンティックエラー? それらすべてを見つけて、ユーザーに報告する必要があります。 すべてが順調に進んだ場合でも、シンボルを解決する必要があります。 たとえば、「リスト」はjava.util.Listまたはjava.awt.Listを指しますか? オーバーロードされたメソッドを呼び出すとき、どれを呼び出しますか? 最後に、プログラムのバイトコードを生成する必要があります。

したがって、ソースコードからコンパイルされたバイトコードまで、3つの主要なフェーズがあります。

  1. ASTの構築
  2. ASTの分析と変換
  3. ASTからのバイトコードの生成

それらのフェーズを詳しく見ていきましょう。

ASTの構築:構文解析は一種の解決された問題です。 そこには多くのフレームワークがありますが、ANTLRを使用することをお勧めします。 これはよく知られており、よく維持されており、文法の指定を容易にするいくつかの機能があります(再帰的なルールは処理されません。理解する必要はありませんが、ありがたいです!)。

ASTの分析と変換:型システムの作成、検証、シンボル解決は困難であり、かなりの作業が必要になる可能性があります。 このトピックだけでも、別の投稿が必要になります。 今のところ、これはあなたがほとんどの努力を費やそうとしているあなたのコンパイラの一部であると考えてください。

ASTからバイトコードを生成する:この最後のフェーズは実際にはそれほど難しくありません。 基本的に、変換されたASTの単一ノードを1つまたはいくつかのバイトコード命令に変換できるように、前のフェーズでシンボルを解決し、地形を準備しておく必要があります。 forループ、スイッチ、ifなどを条件付きおよび無条件のジャンプのシーケンスで変換するため、制御構造には追加の作業が必要になる場合があります(はい、美しい言語の下にはまだたくさんのgotoがあります)。 JVMが内部でどのように機能するかを学ぶ必要がありますが、実際の実装はそれほど難しくありません。

他の言語との統合

あなたがあなたの言語の世界支配を獲得するとき、すべてのコードはそれを排他的に使用して書かれます。 ただし、中間ステップとして、あなたの言語はおそらく他のJVM言語と一緒に使用されます。 おそらく誰かが、より大きなプロジェクトの中で、あなたの言語でいくつかのクラスや小さなモジュールを書き始めるでしょう。 複数のJVM言語を混在させることができると期待するのは合理的です。 それで、それはあなたの言語ツールにどのように影響しますか?

2つの異なるシナリオを検討する必要があります。

  • あなたの言語と他の言語は別々にコンパイルされたモジュールに住んでいます
  • あなたの言語と他の言語は同じモジュールに住んでいて、一緒にコンパイルされます

最初のシナリオでは、コードは他の言語で記述されたコンパイル済みコードのみを使用する必要があります。 たとえば、Guavaや同じプロジェクト内のモジュールなどの一部の依存関係は個別にコンパイルできます。 この種の統合には2つのことが必要です。1つは、他の言語で生成されたクラスファイルを解釈してシンボルを解決し、それらのクラスを呼び出すためのバイトコードを生成できるようにする必要があります。 2番目のポイントは最初のポイントに鏡面反射です。他のモジュールは、コンパイル後に自分の言語で記述されたコードを再利用したい場合があります。 現在、Javaはほとんどのクラスファイルと対話できるため、通常は問題になりません。 ただし、JVMには有効であるが、Javaから呼び出すことはできないクラスファイルを作成することはできます(たとえば、Javaでは無効な識別子を使用しているため)。

2番目のシナリオはもっと複雑です。Javaコードで定義されたクラスAと、自分の言語で記述されたクラスBがあるとします。 2つのクラスが相互に参照しているとします(たとえば、AはBを拡張でき、Bは同じメソッドのパラメーターとしてAを受け入れることができます)。 ここで重要なのは、Javaコンパイラはご使用の言語のコードを処理できないため、クラスBのクラスファイルを提供する必要があるということです。ただし、クラスBをコンパイルするには、クラスAへの参照を挿入する必要があります。ある種の部分的なJavaコンパイラを使用するには、Javaソースファイルがそれを解釈し、クラスBのコンパイルに使用できるモデルを生成できます。これにはJavaコードを解析できる必要があることに注意してください( JavaParserのようなもの)そしてシンボルを解きます。 どこから始めればよいかわからない場合は、java-symbol-solverを参照してください。

ツール:Gradle、Maven、テストフレームワーク、CI

幸いなことに、gradleまたはmaven用のプラグインを開発することで、ユーザーが自分の言語で記述されたモジュールを使用しているという事実をユーザーに対して完全に透過的にすることができます。 プログラミング言語でファイルをコンパイルするようにビルドシステムに指示できます。 ユーザーはmvncompileまたはgradleassembleを実行し続け、違いに気付くことはありません。

悪いニュースは、Mavenプラグインの作成が簡単ではないことです。ドキュメントは非常に貧弱で、理解できず、ほとんどが古くなっているか、単に間違っています。 はい、それは慰めに聞こえません。 私はまだgradleプラグインを作成していませんが、はるかに簡単なようです。

ビルドシステムを使用してテストを実行する方法も検討する必要があることに注意してください。 テストをサポートするには、単体テストの非常に基本的なフレームワークを考え、それをビルドシステムと統合して、mavenテストを実行すると、言語でテストを検索し、コンパイルして実行し、出力をユーザーに報告する必要があります。

私のアドバイスは、利用可能な例を確認することです。そのうちの1つは、トリノプログラミング言語用のMavenプラグインです。

実装すると、誰もが自分の言語で記述されたソースファイルを簡単にコンパイルし、Travisなどの継続的インテグレーションサービスで使用できるようになります。

IDEプラグイン

IDEのプラグインは、ユーザーにとって最も目に見えるツールであり、言語の認識に大きな影響を与えるものになります。 優れたプラグインは、スマートオートコンプリート、コンテキストエラー、および提案されたリファクタリングを提供することにより、ユーザーが言語を学習するのに役立ちます。

現在、最も一般的な戦略は、1つのIDE(通常はEclipseまたはIntelliJ IDEA)を選択し、そのための特定のプラグインを開発することです。 これはおそらくツールチェーンの中で最も複雑な部分です。 これにはいくつかの理由があります。まず、あるIDE用のプラグインを他のIDE用に開発するために費やす作業を合理的に再利用することはできません。 EclipseとIntelliJプラグインは完全に分離されます。 2つ目のポイントは、IDEプラグインの開発はあまり一般的ではないため、ドキュメントが少なく、コミュニティが小さいということです。 それはあなたが自分で物事を理解するのに多くの時間を費やさなければならないことを意味します。 私はEclipseとIntelliJIDEA用のプラグインを個人的に開発しました。 Eclipseフォーラムでの私の質問は、何ヶ月も何年も答えられないままでした。 IntelliJフォーラムでは、運が良かったのですが、開発者から回答が得られることもありました。 ただし、プラグイン開発者のユーザーベースは小さく、APIは非常にビザンチンです。 苦しむ準備をしなさい。

これに代わる方法があり、Xtextを使用することです。 Xtextは、Eclipse、IntelliJ IDEA、およびWeb用のプラグインを開発するためのフレームワークです。 Eclipseで生まれ、最近他のプラットフォームをサポートするように拡張されたばかりなので、それほど経験はありませんが、検討する価値のある代替手段になる可能性があります。 これを簡単に説明します。非常に優れたプラグインを開発する唯一の方法は、各IDEのネイティブAPIを使用してプラグインを開発することです。 ただし、Xtextを使用すると、わずかな労力でかなり適切なものを作成できます。言語の構文にそれを与えるだけで、構文エラー/完了を無料で取得できます。 それでも、シンボル解決と難しい部分を実装する必要がありますが、これは非常に興味深い出発点です。 ただし、難しいのは、Javaシンボルを解決するためのプラットフォーム固有のライブラリとの統合であるため、これですべての問題が解決されるわけではありません。

結論

あなたの言語に興味を示した潜在的なユーザーを失う可能性のある多くの方法があります。 新しい言語を採用することは、それを学び、私たちの開発習慣を適応させる必要があるため、挑戦です。 消耗を可能な限り減らし、ユーザーにすでに知られているエコシステムを活用することで、ユーザーがあなたの言語を学び、恋に落ちる前に、ユーザーが諦めないようにすることができます。

理想的なシナリオでは、ユーザーは自分の言語で記述された単純なプロジェクトのクローンを作成し、標準ツール(MavenまたはGradle)を使用して違いに気付かずにプロジェクトを構築できます。 プロジェクトを編集したい場合は、お気に入りのエディターで開くことができます。プラグインは、エラーを指摘し、スマートな補完を提供するのに役立ちます。 これは、コンパイラを呼び出してメモ帳を使用してファイルを編集する方法を理解する必要があるシナリオとは大きく異なります。 あなたの言語を取り巻くエコシステムは本当に違いを生むことができます、そして今日それは合理的な努力で構築することができます。

私のアドバイスは、あなたの言語では創造的であることですが、あなたのツールでは創造的ではありません。 使い慣れた標準を使用して、人々があなたの言語を採用するために直面​​しなければならない最初の困難を減らします。

幸せな言語デザイン!


Toptal Engineeringブログでさらに読む:

  • インタプリタを最初から書く方法