本篇内容主要讲解“如何理解编程语言的IDE支持”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“如何理解编程语言的IDE支持”吧!
语法分析
对于开发工具来说,语法分析有几个重要的功能:
语法高亮,是指根据术语类别来显示不同的颜色与字体以增强可读性的一种编辑器特性。
实现智能感知
实现跳转和引用分析
从我粗糙的调查来看,大致可以分析为四类:
基于正则表达式来实现语法分析
Sublime Text 基于 YAML 形式的正则匹配方式:Sublime Syntax files
Textmate、VS Code 基于 JSON 的正则匹配方式:Language Grammars
基于语法分析器(如 BNF)生成中间代码
Jetbrins 基于 BNF 生成代码的方式:Grammar and Parser
自制 DSL 进行语法解析
Vim 基于正则 + 自制 DSL:Vim documentation: syntax、Rust 示例
手写解析语法
Eclipse IDE 提供了个 JFace editor,但是似乎是要手写:FAQ How do I provide syntax coloring in an editor?
Emacs Mode: ModeTutorial
每一类各自有各自的优缺点和编写难度。但是,总的来说,没有一个方式是简单的。
正则实现语法分析
对于正则方式来说,不论是 Sublime Text 还是 Textmate 及基于 Textmate 语法规则的 VS Code,它们都有一个显著的缺点:长,如 VCode 的java.tmLanguage.json,从长度上来说,我看到的这个版本有 1831 行。表达方式也有些繁琐:
"comments": { "patterns": [ { "captures": { "0": { "name": "punctuation.definition.comment.java" } }, "match": "/\\*\\*/", "name": "comment.block.empty.java" }, { "include": "#comments-inline" } ] },
其中还有各种 include 关系等。对于 Sublime Text 也是类似的:
comments: - match: /\*\*/ scope: comment.block.empty.java punctuation.definition.comment.java - include: scope:text.html.javadoc - include: comments-inline
看了看,是不是会怀疑他们建立了语法同盟。
但是呢,yaml 和 json 是一个编程语言无关的东西。所以,VS Code 和 Atom 可以基于 Textmate 语法规则,快速建立对于主流语言的词法分析,从而建立了语法高亮的支持。
我们也可以说 BNF 是一种编程语言无关的东西。但是,实际上在我们操作的时候,就会加入一些编程语言特定的要素。
语法分析器分析
由于先前编写系统分析工具 Coca 和通用语法分析器 Chapi ,我对于 BNF 的词法也是颇为上手的——实际上不难。唯一麻烦的地方就是,写完之后,我们要编写代码做一些转换,所以让我们来看看 Jetbrians 插件的示例:
COMMENT = 'regexp://[^\r\n]*' BLOCK_COMMENT = 'regexp:[/][*][^*]*[*]+([^/*][^*]*[*]+)*[/]'
这一点上和 antlr 没有太大的区别:
WS: [ \t\r\n\u000C]+ -> channel(HIDDEN); COMMENT: '/*' .*? '*/' -> channel(HIDDEN); LINE_COMMENT: '//' ~[\r\n]* -> channel(HIDDEN);
然后,就是设计和分析词法了:
functionParameters ::= LPAREN inputParameters RPAREN outputParameters? | IN SUB GT inputParameters | outputParameters
接着,在 IDEA 里面,我们可以通过这个 BNF 文件生成对应的 Lexer 文件和代码等。对于使用 Antlr 编写的词法来说,Java 部分的代码规模也就在 800 左右。
不过呢,从两者的阅读体验对比来看,显然 BNF 会更加友好一点。
自制 DSL 语法解析
颇为遗憾的是,我尚未写过任何的 Vim 插件,好在我还知道 Vim 是如何退出来的。我使用 Vim 作为 git 的 editor,还熟知一些 Vim 编辑的常用快捷键。所以,语法高亮这一部分主要是参考 Vim 的文档编写和代码示例。这里我找到了一个不错的中文翻译:语法高亮
总的来说,语法规则就是: syn vim关键字 匹配规则,如:
syn region rustCommentLine start="//" end="$" contains=rustTodo,@Spell syn region rustCommentLineDoc start="//\%(//\@!\|!\)" end="$" contains=rustTodo,@Spell syn region rustCommentLineDocError start="//\%(//\@!\|!\)" end="$" contains=rustTodo,@Spell contained syn region rustCommentBlock matchgroup=rustCommentBlock start="/\*\%(!\|\*[*/]\@!\)\@!" end="\*/" contains=rustTodo,
看上去依旧是正则匹配,如 Float:
syn match rustFloat display "\<[0-9][0-9_]*\%(\.[0-9][0-9_]*\)\%([eE][+-]\=[0-9_]\+\)\=\(f32\|f64\)\=" syn match rustFloat display "\<[0-9][0-9_]*\%(\.[0-9][0-9_]*\)\=\%([eE][+-]\=[0-9_]\+\)\(f32\|f64\)\=" syn match rustFloat display "\<[0-9][0-9_]*\%(\.[0-9][0-9_]*\)\=\%([eE][+-]\=[0-9_]\+\)\=\(f32\|f64\)"
不过,从算法形式上来说,完胜 Textmate 和 Sublime,毕竟是高级的 DSL。
编程语言语法解析
Emacs 的 mode 里包含了对于语法高亮的处理,于是为了这个高亮,我们需要写写 emacs lisp 代码。如:
(defvar rust-formatting-macro-opening-re "[[:space:]\n]*[({[][[:space:]\n]*" "Regular expression to match the opening delimiter of a Rust formatting macro.") (defvar rust-start-of-string-re "\\(?:r#*\\)?\"" "Regular expression to match the start of a Rust raw string.")
对于 Eclipse 来说,这个过程就更加麻烦了。
语言的高级支持
在我们实现了开发工具的词法分析接口之后,我们就能按不同的 IDE/编辑器所定义的接口,进行定制了。这是一个繁杂,而又充满挑战的工作。对于不同的工具来说,它们的接口相关也甚多。我也并非都能一一了解 API,所以只能简单的以 IDEA 作为一个示例来展示。主要原因大概有两个:1. 我日常使用的是 Jetbrains 相关的 IDE;2. 我已经有一部分代码了。
语法高亮
在进行了复杂的语法分析之后,接着,我们就可以快速进入一个简单的环节,对代码进行高亮。关于高亮的话,我们可以快速进行一个分类:
关键词。即编程语言的关键词,如 C 语言中的 32 个关键词。
标识符。用户定义的字符串,如变量名、结构体名、函数名等等。
特殊词法。
重要的词法。根据需要,可以针对于函数名、静态函数名等进行标识,以提升识别度。
如下是 Go 语言的一些关键词:
(defconst go-mode-keywords '("break" "default" "func" "interface" "select" "case" "defer" "go" "map" "struct" "chan" "else" "goto" "package" "switch" "const" "fallthrough" "if" "range" "type" "continue" "for" "import" "return" "var") "All keywords in the Go language. Used for font locking.")
所以,在这个场景之下,不论是何种的 IDE 又或者是编辑器都可以快速实现。
跳转 goto
不同开发工具,有各种的跳转规则,不同的语言也有各自的跳转方式。如 Emacs 的 go-mode 就定义了一系列的跳转:
(let ((m (define-prefix-command 'go-goto-map))) (define-key m "a" #'go-goto-arguments) (define-key m "d" #'go-goto-docstring) (define-key m "f" #'go-goto-function) (define-key m "i" #'go-goto-imports) (define-key m "m" #'go-goto-method-receiver) (define-key m "n" #'go-goto-function-name) (define-key m "r" #'go-goto-return-values))
而 IDEA 也提供了一系列接口来实现类似的功能,如:
gotoActionAliasMatcher gotoClassContributor gotoSymbolContributor gotoFileContributor gotoRelatedProvider
我们只需要分析光标符所在的位置,其所定义的语法,如 IDEA 里是 PSI,再实现对应的逻辑即可。如:
@Override public @NotNull NavigationItem[] getItemsByName(String name, String pattern, Project project, boolean includeNonProjectItems) { List<CharjStructDeclaration> properties = findStructByKey(project, name); return properties.toArray(new NavigationItem[properties.size()]); }
这里定义的是数据结构的导航。当我们按下快捷键的时候,会传入 name、pattern 等信息。接着,从所有相关的文件(VirtualFile)中寻找对应的 struct,返回即可。
自动填充
主要可以分为两类,一类是:代码段(Snippets),一类是:自动填充(Completion)
好像也没啥说的,就是绑定在特定关键字上的内容。
其它
剩下的就是一些比较有意思的功能,诸如于:
fileType。文件图标支持。即某一类型的文件,使用特定的图标来展示。
commet 。即按下注释的快捷键,能快速的注释和反注释代码。
line marker。IDEA 提供的功能,用于在行上通过图标来展示特定的功能。
folding。提供特定的代码段的折叠功能。
数据视图。展示特定数据结构关系及参数等的视图。
……
到此,相信大家对“如何理解编程语言的IDE支持”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。