アイデアのために独自の言語プラグインを書く方法(その1 )
導入
私はバックエンド開発者として働いています、そして、時々、私は仕事ルーチンに疲れています.いくつかのオープンソースに深まって、私は本当にJetbrainsの製品やアイデアに熱心に役立ちます.アイデアを自分で書くことができるプラグインでカスタマイズすることができます.
アイデアのコードの大部分はオープンソースです.Jetbrainsから開発者は、常にプルの要求を作成し、メインツールを改善するのに役立ちます.
私は既に1つの検査で書く方法に関する記事を持っています.しかし、あなたがアイデアがどのように働くかを理解したいならば、あなたは言語プラグインを書くようにしなければなりません.
これをしましょう.
「モルモット」としては簡単な言葉を使うMonkey , インタプリタとコンパイラはbooks on Golang . 私の目標はすべてをカバーすることではなかったので、プラグインは、この言語のいくつかの限られたサブセットをカバーしています.インタプリタが見つかるhere .
猿のFibonacci数の計算例
let fibonacci = fn(x){
if (x == 0){
0;
}
else{
if (x == 1){
return 1;
}
else{
fibonacci(x - 1) + fibonacci(x - 2);
};
};
};
このプラグインをjavaとkotlin(jvm)で書きます.
必要条件
新しいプラグインを作成する最も簡単な方法はthe template . これは積極的に開発されており、すでに最も必要な機能が含まれていた.
また、Devkitプラグインが必要です
Grammar-Kit プラグインは、辞書と構文解析器を生成するために使用されます
他のプラグインの例
let fibonacci = fn(x){
if (x == 0){
0;
}
else{
if (x == 1){
return 1;
}
else{
fibonacci(x - 1) + fibonacci(x - 2);
};
};
};
新しいプラグインを作成する最も簡単な方法はthe template . これは積極的に開発されており、すでに最も必要な機能が含まれていた.
また、Devkitプラグインが必要です
Grammar-Kit プラグインは、辞書と構文解析器を生成するために使用されます
他のプラグインの例
Java (考えのコードで)
go-plugin (このプラグインはGolandとして知られています).
Monkey plugin この記事では
言語プラグインの基礎を作成する
言語プラグインを作成する最初の段階はthe documentation .
import com.intellij.lang.Language
class MonkeyLanguage : Language("Monkey") {
companion object {
@JvmStatic
val INSTANCE = MonkeyLanguage()
}
}
import com.intellij.openapi.fileTypes.LanguageFileType
import javax.swing.Icon
class MonkeyFileType : LanguageFileType(MonkeyLanguage.INSTANCE) {
override fun getName(): String {
return "Monkey File"
}
override fun getDescription(): String {
return "Monkey language file"
}
override fun getDefaultExtension(): String {
return "monkey"
}
override fun getIcon(): Icon {
return MonkeyIcons.FILE
}
companion object {
@JvmStatic
val INSTANCE = MonkeyFileType()
}
}
その後、拡張ポイントを介して新しいファイルタイプを有効にする必要があります.プラグインが提供するすべての機能は、1つ以上の拡張ポイントを介して有効になります.ファイルプラグインで宣言しなければなりません.XML (例)go-plugin , frege ). エクステンションポイントを使用する他の例を以下に示しますdocumentation .サル( resource/meta - inf/plugin . xml )の例
<extensions defaultExtensionNs="com.intellij">
<fileType name="Monkey File"
implementationClass="com.github.pyltsin.monkeyplugin.MonkeyFileType"
fieldName="INSTANCE"
language="Monkey"
extensions="monkey"/>
</extensions>
クリエーションツリー
残念ながら、1つの記事のコードを解析するのに必要な全体の理論を記述することは不可能です.このトピックの基本的な知識はDragon Book "
この本によると、コードで作業するコンパイラのプロセスは次の手順から成ります.
PSI木を作成するプロセスは、図からの図によく示されますdocumentation :
PSIツリーを見るにはPSI Viewer ( Tools -> view psiの構造)
考えでは、抽象的なTreeElementクラスは主にPSI木を実装するのに用いられます
public abstract class TreeElement extends ElementBase implements ASTNode, Cloneable {
private TreeElement myNextSibling;
private TreeElement myPrevSibling;
private CompositeElement myParent;
...
}
アイデアでは、あなたはGrammarKit Lexerとパーサーを作成するプラグイン.レクサー
おそらく、アイデアのためのレクチャーを作成する最も簡単な方法はJFlex . GrammarkItプラグインには既に実装が含まれており、lexerを生成することができます.BNFファイルまたはから.フレックスファイル(より多くのカスタマイズオプションで).猿の言語の例を見ることができますhere , フレージュのためのより複雑なものhere .
Lexerを生成するには、Gradleプラグインを設定するか、コンテキストメニューを使用する必要があります.フレックスファイル-“JFlexジェネレータを実行する”.
その後、実装するクラスを宣言する必要がありますcom.intellij.lexer.Lexer
. 既に生成されたjflex lexerのアダプタがあります.com.intellij.lexer.FlexAdapter
パーサー
アイデアでは、Grammarkitプラグインによるコード生成は、主にパーサーを作成するために使用されます.残念ながら、それについて多くのドキュメンテーションがありません、そして、それは2011年に発表されるだけですTutorial and HOWTO .
言語の文法についてはBNF . 使用される唯一の違いは::=
として.
式の文法例here
ご覧のように、BNFファイルは2つの部分から成ります.
アイデアでは、Grammarkitプラグインによるコード生成は、主にパーサーを作成するために使用されます.残念ながら、それについて多くのドキュメンテーションがありません、そして、それは2011年に発表されるだけですTutorial and HOWTO .
言語の文法についてはBNF . 使用される唯一の違いは
::=
として.式の文法例here
ご覧のように、BNFファイルは2つの部分から成ります.
parserClass
- 生成されたパーサクラスの名前と場所parserUtilClass
- パーサのための追加メソッドのセットを含むクラスへの参照com.intellij.lang.parser.GeneratedParserUtilBase
クラスまたはその継承子)extends = <some class>
- すべてのPSI要素(ツリーノード)が継承される基底クラスへのリファレンス.通常com.intellij.extapi.psi.ASTWrapperPsiElement
またはその継承子.extends(<regexp for tree nodes>) = <psi-element>
(例:extends(".*expr")=expr
) - すべてのpsi要素は指定されたpsi要素から継承されます.psiClassPrefix
, psiImplClassSuffix
- クラスとインタフェースのための接頭辞(通常、言語の名前による)とインターフェイスを実装するための接尾辞(通常)Impl
)psiPackage
and psiImplPackage
インタフェース用のパッケージとその実装です.implements
- 同様extends
, インターフェイスelementTypeHolderClass
- 要素のすべての型の生成されたストレージelementTypeClass
- Elemets型のクラス(生成されない、継承するクラス)com.intellij.psi.tree.IElementType
)elementTypeFactory
- 要素の型を生成するファクトリを作成するpsiImplUtilClass
- psi要素の必須メソッドの実装として使用される静的メソッドのセットを持つクラス.このような行があるとしましょう
ImportSpec ::= [ '.' | identifier ] ImportString {
stubClass="com.goide.stubs.GoImportSpecStub"
methods=[getAlias getLocalPackageName shouldGoDeeper isForSideEffects isDot getPath getName isCImport]
}
そのためには、psiImplUtilClass
:public static String getAlias(@NotNull GoImportSpec importSpec)
その後getAlias
は以下のように生成されます: public String getAlias() {
return GoPsiImplUtil.getAlias(this);
}
それでは、BNFのルール自体に移りましょう.修飾子は、各ルールに対して使用できます.private
, fake
, など).その説明はhere . 例えばprivate
インprivate boolean_group ::= xor_expr | between_expr | is_not_expr
PSI要素がboolean_group
は生成されません.BNFファイルで文法を正しく記述することができないなら、それは外部規則を使用してコードでそれを記述するのに役に立つでしょう.
文法の重要な部分の1つは、エラーを扱うための規則です.つのキーワードが使われます:
pin
and recoverWhile
.pin
- トークン番号を示します.例えば、ゴングで構造を宣言するStruct Type ::= struct '{' Fields? '}' {pin=1}
recoverWhile
- すべてのルールが一致した後にトークンを消費できるかどうかを指定します.この属性の使用に関する推奨事項についてはhere .また、あなたは注意を払うべきですrecommendations 優先度に基づく構文解析用.
将来の仕事のための正確で便利な文法規則を作成することは、言語のためにプラグインを実装する最も難しい部分のうちの1つであると私に思われます.始めるには、以下のようにします.go-plugin , Frege , Monkey (猿に対しては、この言語のサブセットのみを簡略化するために実装しています).
BNFを作成してからパーサーを生成した後、
File
相続人com.intellij.extapi.psi.PsiFileBase
) (例からgo-plugin , Frege , Monkey ) とパーサの定義クラスcom.intellij.lang.ParserDefinition
) 例go-plugin , Frege , Monkey ), して拡張ポイントを介して有効にします.<lang.parserDefinition language="Monkey"
implementationClass="com.github.pyltsin.monkeyplugin.parser.MonkeyParserDefinition"/>
Reference
この問題について(アイデアのために独自の言語プラグインを書く方法(その1 )), 我々は、より多くの情報をここで見つけました https://dev.to/pyltsinm/how-to-write-your-own-language-plugin-for-idea-part-1-dieテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol