idea使用monaco
这是关于为我们将要定义的新语言创建基于浏览器的编辑器的教程。
我们将使用两个组件:
我们将为一种简单的语言构建一个编辑器以执行计算。 结果将是这样的:
所有代码均可在线获得: calc-monaco-editor 。
前段时间,我们写了一篇文章,关于在浏览器中使用ANTLR构建简单的Web编辑器 。 我们在下一步的基础上继续进行这项工作,使其适应NPM和WebPack的使用,从而简化了构建应用程序的过程。
为什么要使用摩纳哥?
摩纳哥(Monaco)源自Visual Studio Code(VSCode,为其朋友)。 VSCode是一个轻量级的编辑器,正在吸引越来越多的用户。 摩纳哥基本上将VSCode重新打包以在浏览器中运行。 它是一款出色的编辑器,具有许多有趣的功能,维护得当,并根据许可的开源许可证(MIT许可证)发布。
为什么要使用ANTLR?
ANTLR是一种工具,给定语法可以生成多种目标语言的相应解析器。 除其他外,还支持Java,C#,Python和Javascript。 另一个附加价值是ANTLR有几种语法可用。 因此,如果您学习如何将摩纳哥语和ANTLR结合起来,则可以轻松地在摩纳哥获得对任何可以找到ANTLR语法的语言的支持。 我们提供了有关ANTLR的大量材料,从免费的ANTLR Mega教程到视频课程,以像专业人士一样学习ANTLR 。
我们的样本项目
让我们修改一下如何组织项目:
- 在此项目中,我们将使用NPM下载依赖项,例如ANTLR运行时
- 我们将使用gradle调用ANTLR工具,该工具将从语法定义中为我们的语言生成Javascript解析器
- 我们将使用TypeScript编写代码。 我们的代码会将ANTLR连接到摩纳哥
- 我们将使用WebPack将Javascript打包在一个文件中
- 我们将使用Mocha编写单元测试
- 我们将使用Kotlin和ktor框架编写一个更简单的服务器。 只要有机会,我就可以选择与Kotlin一起使用,这可以由任何服务器代替。
我们简单的计算语言
我们的语言会非常简单。 它只允许执行非常简单的计算。 这是有意的,但是相同的方法可以用于非常复杂的语言。
我们将能够编写如下代码:
input a
b = a * 2
c = (a - b) / 3
output c
实际上,我们的语言将允许定义:
- 输入:它们是计算器要接收的值
- 计算:可以计算新值并将其存储在变量中。 可以从输入或其他变量中计算得出
- 输出:我们确定要作为计算结果返回的变量
编写解析器
首先,我们将从定义词法分析器和解析器的语法开始。
我们将创建目录src/main/antlr
并在该目录中定义文件CalcLexer.g4
和CalcParser.g4
。
我们不会从头开始解释如何编写ANTLR语法。 如果您不熟悉ANTLR,则可以从ANTLR Mega Tutorial开始。 但是,我们有一些针对此用例的注释,特别是在词法分析器上。
- 我们不应该跳过空格,而应该将这些标记插入特定的频道,因为所有标记都将与语法突出显示相关。
- 另外,词法分析器应将每个字符都赋予一个令牌,这就是为什么我们在词法分析器的末尾添加一个特殊规则以捕获任何其他词法分析器规则未捕获的字符的原因。
- 为简单起见,我们应避免标记跨越多行或使用词法模式,因为它们会使与摩纳哥的集成更难于语法突出显示。 这些是我们可以解决的问题(我们也可以解决客户的项目),但是我们不想在本教程中解决它们,因为它们会使您更加难以理解基础知识
这是我们的词法分析器语法( CalcLexer.g4
):
lexer grammar CalcLexer;
channels { WS_CHANNEL }
WS: [ \t]+ -> channel(WS_CHANNEL);
NL: ('\r\n' | '\r' | '\n') -> channel(WS_CHANNEL);
INPUT_KW : 'input' ;
OUTPUT_KW : 'output' ;
NUMBER_LIT : ('0'|[1-9][0-9]*)('.'[0-9]+)?;
ID: [a-zA-Z][a-zA-Z0-9_]* ;
LPAREN : '(' ;
RPAREN : ')' ;
EQUAL : '=' ;
MINUS : '-' ;
PLUS : '+' ;
MUL : '*' ;
DIV : '/' ;
UNRECOGNIZED : . ;
这是我们的解析器语法( CalcParser.g4
):
parser grammar CalcParser;
options { tokenVocab=CalcLexer; }
compilationUnit:
(inputs+=input)*
(calcs+=calc)*
(outputs+=output)*
EOF
;
input:
INPUT_KW ID
;
output:
OUTPUT_KW ID
;
calc:
target=ID EQUAL value=expression
;
expression:
NUMBER_LIT
| ID
| LPAREN expression RPAREN
| expression operator=(MUL|DIV) expression
| expression operator=(MINUS|PLUS) expression
| MINUS expression
;
现在我们有了语法,我们需要从中生成Javascript词法分析器。 为此,我们将需要使用ANTLR工具。 对我而言,最简单的方法是使用gradle下载ANTLR及其依赖项,并在gradle中定义任务以调用ANTLR。
我们将通过运行以下命令安装gradle包装器:
gradle wrapper --gradle-version=5.6.1 --distribution-type=bin
build.gradle
脚本将如下所示:
apply plugin: 'java'
repositories {
jcenter()
}
dependencies {
runtime 'org.antlr:antlr4:4.7.2'
}
task generateLexer(type:JavaExec) {
def lexerName = "CalcLexer"
inputs.file("$ANTLR_SRC/${lexerName}.g4")
outputs.file("$GEN_JS_SRC/${lexerName}.js")
outputs.file("$GEN_JS_SRC/${lexerName}.interp")
outputs.file("$GEN_JS_SRC/${lexerName}.tokens")
main = 'org.antlr.v4.Tool'
classpath = sourceSets.main.runtimeClasspath
args = ['-Dlanguage=JavaScript', "${lexerName}.g4", '-o', '../../main-generated/javascript']
workingDir = ANTLR_SRC
}
task generateParser(type:JavaExec) {
dependsOn generateLexer
def lexerName = "CalcLexer"
def parserName = "CalcParser"
inputs.file("$ANTLR_SRC/${parserName}.g4")
inputs.file("$GEN_JS_SRC/${lexerName}.tokens")
outputs.file("$GEN_JS_SRC/${parserName}.js")
outputs.file("$GEN_JS_SRC/${parserName}.interp")
outputs.file("$GEN_JS_SRC/${parserName}.tokens")
main = 'org.antlr.v4.Tool'
classpath = sourceSets.main.runtimeClasspath
args = ['-Dlanguage=JavaScript', "${parserName}.g4", '-no-listener', '-no-visitor', '-o', '../../main-generated/javascript']
workingDir = ANTLR_SRC
}
它使用gradle.properties
文件中定义的一些属性:
ANTLR_SRC = src/main/antlr
GEN_JS_SRC = src/main-generated/javascript
实际上,这将使用src/main/antlr
下的语法来生成src/main-generated/javascript
下的词法分析器。
我们可以运行ANTLR:
./gradlew generateParser
这也将产生词法分析器,作为任务generateParser
任务对任务的依赖性generateLexer
。
运行此命令后,您应该将这些文件放在src/main-generated/javascript
:
- CalcLexer.interp
- CalcLexer.js
- CalcLexer.tokens
- CalcParser.interp
- CalcParser.js
- CalcParser.tokens
使用NPM管理依赖关系
为了运行我们的词法分析器和解析器,我们需要做两件事:生成的Javascript代码和ANTLR运行时。 为了获得ANTLR运行时,我们将使用NPM。 NPM也将用于下载摩纳哥。 因此,我们不会为项目运行Node.JS,我们只会使用它来获取依赖关系并运行测试。
我们将假定您已经在系统上安装了npm。 如果没有,那么就该打谷歌,弄清楚如何安装它。
安装npm后,我们需要通过填充package.json
文件来提供项目配置:
{
"name": "calc-monaco-editor",
"version": "0.0.1",
"author": "Strumenta",
"license": "Apache-2.0",
"repository": "https://github.com/Strumenta/calc-monaco-editor",
"dependencies": {
"antlr4": "^4.7.2",
"webpack": "^4.39.2",
"webpack-cli": "^3.3.7"
},
"devDepe