Kanvas:从您的ANTLR语法生成一个简单的IDE

什么是编辑器?

对我来说,编辑器是我工作中使用的主要工具。 作为语言工程师,我创建新的语言,使用现有的语言,并且需要其他工具来使用它们。 我希望能够在一个定制的IDE中将所有这些黑客一起入侵,我可以为我成长。 这就是为什么我要使用可**的编辑器Kanvas的原因。 当然在GitHub上

在许多情况下,我需要一个用于DSL的简单文本编辑器,并且倾向于使用ANTLR构建它们。 我将需要其他东西,例如表格或图形投影,模拟器等,但是我需要从某个地方开始,对吗? 另外,我认为目前还没有一种简单的方法来获得具有最小依赖性和简单结构的DSL独立编辑器。 菜单上没有灯光选项。 是时候添加一个了。

快速从语法中获取编辑器

定义语言语法后,您可以从中提取很多信息。 我认为您应该能够免费从中获得尽可能多的价值,并可以根据需要进一步自定义它。 这类似于Xtext的想法(减去理解EMF所需阅读的400页)。

您能多快获得ANTLR语法的编辑器? 您为编辑器创建一个新项目,将Kanvas添加为依赖项,并注册您打算支持的语言:

fun main(args: Array<String>) {
    languageSupportRegistry.register("sm", smLangSupport)
    val kanvas = Kanvas()
    SwingUtilities.invokeLater {
        kanvas.createAndShowKanvasGUI()
        kanvas.addTab("My SM", languageSupport = smLangSupport)
    }

并添加以下行以支持您的语言:

object smLangSupport : BaseLanguageSupport() {
    override val antlrLexerFactory: AntlrLexerFactory
        get() = object : AntlrLexerFactory {
            override fun create(code: String): Lexer = SMLexer(org.antlr.v4.runtime.ANTLRInputStream(code))
        }
    override val parserData: ParserData?
        get() = ParserData(SMParser.ruleNames, SMParser.VOCABULARY, SMParser._ATN)
}

这样很快。 少于10行代码。 我们只需要指定Lexer和Parser类(在此示例中为SMLexerSMParser )。

如果您想知道那是什么语言,那就是Kotlin:一种用于JVM的简洁静态语言,可以轻松与Java互操作。

让我们对其进行一些改进:语法突出显示

因此,我有一种简单的语言,基本上可以免费获得编辑器,然后开始使用它。 首先,我想为不同种类的标记定义样式。 我们正在做一些简单的事情,只需设置颜色即可:

object smLangSyntaxScheme : SyntaxScheme(true) {
    override fun getStyle(index: Int): Style {
        val style = Style()
        val color = when (index) {
            // Types
            SMLexer.STRING, SMLexer.INT, SMLexer.DECIMAL -> Color(42, 53, 114)
 
            // Literals
            SMLexer.STRINGLIT -> Color(21, 175, 36)
            SMLexer.INTLIT, SMLexer.DECLIT -> Color.BLUE
 
            // Comments
            SMLexer.COMMENT -> Color(170, 181, 171)
 
            // Operators
            SMLexer.ASTERISK, SMLexer.DIVISION, SMLexer.PLUS, SMLexer.MINUS -> Color.WHITE
 
            // Keywords
            SMLexer.VAR -> Color.GREEN
            SMLexer.INPUT -> Color(200, 250, 200)
            SMLexer.SM -> Color(200, 250, 200)
            SMLexer.EVENT -> Color(200, 250, 200)
            SMLexer.AS -> Color(50, 12, 96)
 
            // Identifiers
            SMLexer.ID -> Color.MAGENTA
 
            // Separators
            SMLexer.ARROW -> Color(50, 12, 96)
            SMLexer.COLON -> Color(50, 12, 96)
            SMLexer.ASSIGN -> Color(50, 12, 96)
            SMLexer.LPAREN, SMLexer.RPAREN -> Color.WHITE
 
            // Rest
            SMLexer.UNMATCHED -> Color.RED
            else -> null
        }
        if (color != null) {
            style.foreground = color
        }
        return style
    }
}

我们没有将某些标记设置为粗体或斜体,因为我们想使事情简单。 顺便说一句,如果您对Kanvas中语法突出显示的工作方式感兴趣,我将在本文中进行介绍。

然后是自动补全

现在,我们免费获得了一些有限的自动完成功能。 基本上,我们会根据语言的结构获得自动补全功能,因此我们的算法可以告诉我们哪些关键字可以插入当前位置,或者在某个位置可以接受标识符。 该算法不能免费确定的是应该建议哪些标识符。 让我们实现一个非常简单的逻辑:当我们可以插入一个标识符时,我们将查看前面的标记并使用它们来确定要提出的建议。 例如,在定义输入时,我们可以建议“ anInput”,而在定义变量时,我们可以建议“ aVar”:

 override val propositionProvider: PropositionProvider
        get() = object : PropositionProvider {
            override fun fromTokenType(completionProvider: CompletionProvider,
                                       preecedingTokens: List<Token>, tokenType: Int): List<Completion> {
                val res = LinkedList<Completion>()
                var proposition : String? = [email protected]!!.vocabulary.getLiteralName(tokenType)
                if (proposition != null) {
                    if (proposition.startsWith("'") && proposition.endsWith("'")) {
                        proposition = proposition.substring(1, proposition.length - 1)
                    }
                    res.add(BasicCompletion(completionProvider, proposition))
                } else {
                    when (tokenType) {
                        SMParser.ID -> {
                            val determiningToken = preecedingTokens.findLast { setOf(SMLexer.SM, SMLexer.VAR, SMLexer.EVENT, SMLexer.INPUT).contains(it.type) }
                            val text = when (determiningToken?.type) {
                                SMLexer.SM -> "aStateMachine"
                                SMLexer.EVENT -> "anEvent"
                                SMLexer.INPUT -> "aInput"
                                SMLexer.VAR -> "aVar"
                                else -> "someID"
                            }
                            res.add(BasicCompletion(completionProvider, text))
                        }
                    }
                }
                return res
            }
        }

这是代码。 这够了吗? 我不知道,但是我所知道的是,这是一个很小的系统,可以理解并且简单到可以轻松扩展和定制。 因此,我计划将其用于这种小型语言,并根据需要改进自动完成功能,尤其是针对该语言。 游戏的名称是有机地和迭代地增长的工具支持。

设计目标:类似于Sublime Text但开源

我们都喜欢Sublime Text。 我想从中得到启发,但是开源。 为什么要开源? 这样我就可以根据需要自定义它。

现在是这样的:

Kanvas:从您的ANTLR语法生成一个简单的IDE

是的,它还没有Sublime Text精美。 但这意味着我还有改进的空间。

到语言工作台还是不到语言工作台?

我经常使用Jetbrains MPS和Xtext等语言工作台。 它们之所以出色,是因为它们允许非常快速地获得非常好的工具支持。 在许多情况下,它们是您的最佳选择。 但是,作为每种工程选择,都需要考虑不同的方面。 Jetbrains MPS和Xtext是非常大而复杂的软件,这种东西重数百MB。 要了解这些平台的内部知识,需要进行大量的工作和大量的努力。 只需使用这些平台,您将获得巨大收益。 但是,它们并不是在所有情况下的最佳解决方案,因为在某些情况下,您需要将语言与现有系统集成在一起,因此,您必须以非设计的方式来弯​​曲这些语言工作台。 也许您想将您的编辑器或工具嵌入到现有平台中,也许您想要在平板电脑上使用一个简单的编辑器,也许您希望从命令行使用这些工具。 也许您想以某种特殊的方式将系统组合在一起以满足您的特定需求。 在这些情况下,使用语言工作台不是正确的选择。 您需要一些简单的东西,可以入侵的东西。 这是我正在尝试的方法。 为此,我正在研究一些开源项目,并写了一本书

结论

这会飞吗? 我不知道。 我很开心地花了一些时间在这个项目上。 我觉得这是为使用ANTLR构建的DSL获得简单的独立编辑器的好方法。 我还想将其用作Kotlin支持的vim,一种新千年的vim。 具有超投射力。 让我们看看它是如何增长的。

是的,我知道Atom将自己描述为可入侵的编辑器。 但是,从我的角度来看,这还不够黑客入侵。

翻译自: https://www.javacodegeeks.com/2017/01/kanvas-generating-simple-ide-antlr-grammar.html