idea插件开发-自定义语言02-Lexer

news2024/11/28 0:53:42

        词法分析器或词法分析器定义文件内容如何分解为标记。词法分析器是自定义语言插件几乎所有功能的基础,比如基本语法突出显示到高级代码分析功能。由Lexer来定义。IDE在三个主要上下文中调用词法分析器,插件可以根据需要提供不同的词法分析器实现:

  • 语法高亮Syntax highlighting:依赖com.intellij.lang.syntaxHighlighterFactory扩展点来实现。
  • 构建文件的语法树:依赖com.intellij.lang.parserDefinition扩展点实现,通过调用其.createLexer()方法;
  • 构建文件中包含的单词的索引:如果使用基于词法分析器的单词扫描器实现,可以将词法分析器传递给DefaultWordsScanner构造函数。

        用于语法高亮显示的词法分析器可以增量调用以仅处理文件的更改部分。相反,在其他上下文中使用的词法分析器总是被调用来处理整个文件或嵌入在不同语言文件中的完整语言结构。

一、Lexer

1、词法分析器状态

        使用增量类型的词法分析器(Syntax highlighting)需要返回其实时状态,即应于文件中每个位置的上下文,比如:java记法分析器可以为顶级上下文、注释上下文和字符串文字上下文设置不同的状态。

        语法高亮词法分析器的状态是由Lexer.getState()返回的单个整数表示。如果从文件中间恢复词法分析时,该状态将与要处理的片段的起始偏移量一起传递给Lexer.start()方法,其它两类分析器一般只需返回0或Lexer.start()即可。

2、如何实现

        使用JFlex是一种简单的为自定义语言插件创建词法分析器方法,可以使用扩展的FlexLexer和FlexAdapter类。另外可以使用 JFlex 的补丁版本idea-flex.skeleton 集成一起实现FlexAdapter。

        开发方法分析器时,可以同时使用 Grammar-Kit plugin 来辅助开发。示例代码

properties.flex

package com.intellij.lang.properties.parsing;

import com.intellij.lexer.FlexLexer;
import com.intellij.psi.tree.IElementType;

%%

%class _PropertiesLexer
%implements FlexLexer
%unicode
%function advance
%type IElementType

CRLF=\R
WHITE_SPACE_CHAR=[\ \n\r\t\f]
VALUE_CHARACTER=[^\n\r\f\\] | "\\"{CRLF} | "\\".
END_OF_LINE_COMMENT=("#"|"!")[^\r\n]*
KEY_SEPARATOR=[:=]
KEY_SEPARATOR_SPACE=\ \t
KEY_CHARACTER=[^:=\ \n\r\t\f\\] | "\\"{CRLF} | "\\".
FIRST_VALUE_CHARACTER_BEFORE_SEP={VALUE_CHARACTER}
VALUE_CHARACTERS_BEFORE_SEP=([^:=\ \t\n\r\f\\] | "\\"{CRLF} | "\\".)({VALUE_CHARACTER}*)
VALUE_CHARACTERS_AFTER_SEP=([^\ \t\n\r\f\\] | "\\"{CRLF} | "\\".)({VALUE_CHARACTER}*)

%state IN_VALUE
%state IN_KEY_VALUE_SEPARATOR_HEAD
%state IN_KEY_VALUE_SEPARATOR_TAIL

%%

<YYINITIAL> {END_OF_LINE_COMMENT}        { yybegin(YYINITIAL); return PropertiesTokenTypes.END_OF_LINE_COMMENT; }

<YYINITIAL> {KEY_CHARACTER}+             { yybegin(IN_KEY_VALUE_SEPARATOR_HEAD); return PropertiesTokenTypes.KEY_CHARACTERS; }
<IN_KEY_VALUE_SEPARATOR_HEAD> {
    {KEY_SEPARATOR_SPACE}+       { yybegin(IN_KEY_VALUE_SEPARATOR_HEAD); return PropertiesTokenTypes.WHITE_SPACE; }
    {KEY_SEPARATOR}             { yybegin(IN_KEY_VALUE_SEPARATOR_TAIL); return PropertiesTokenTypes.KEY_VALUE_SEPARATOR; }
    {VALUE_CHARACTERS_BEFORE_SEP}     { yybegin(YYINITIAL); return PropertiesTokenTypes.VALUE_CHARACTERS; }
    {CRLF}{WHITE_SPACE_CHAR}*  { yybegin(YYINITIAL); return PropertiesTokenTypes.WHITE_SPACE; }
}
<IN_KEY_VALUE_SEPARATOR_TAIL> {
    {KEY_SEPARATOR_SPACE}+      { yybegin(IN_KEY_VALUE_SEPARATOR_TAIL); return PropertiesTokenTypes.WHITE_SPACE; }
    {VALUE_CHARACTERS_AFTER_SEP}   { yybegin(YYINITIAL); return PropertiesTokenTypes.VALUE_CHARACTERS; }
    {CRLF}{WHITE_SPACE_CHAR}*  { yybegin(YYINITIAL); return PropertiesTokenTypes.WHITE_SPACE; }
}
<IN_VALUE> {VALUE_CHARACTER}+            { yybegin(YYINITIAL); return PropertiesTokenTypes.VALUE_CHARACTERS; }
<IN_VALUE> {CRLF}{WHITE_SPACE_CHAR}*     { yybegin(YYINITIAL); return PropertiesTokenTypes.WHITE_SPACE; }
{WHITE_SPACE_CHAR}+                      { return PropertiesTokenTypes.WHITE_SPACE; }
[^]                                      { return PropertiesTokenTypes.BAD_CHARACTER; }

3、Token类型

        词法分析器的标记类型由IElementType来定义,TokenType用来定义语言中的标记类型。对于语言中没有的类型需要创建新的IElementType并与TokenType进行关联。也可以使用TokenSet定义类型组,比如:

public interface PropertiesTokenTypes {
  IElementType WHITE_SPACE = TokenType.WHITE_SPACE;
  IElementType BAD_CHARACTER = TokenType.BAD_CHARACTER;

  IElementType END_OF_LINE_COMMENT = new PropertiesElementType("END_OF_LINE_COMMENT");
  IElementType KEY_CHARACTERS = new PropertiesElementType("KEY_CHARACTERS");
  IElementType VALUE_CHARACTERS = new PropertiesElementType("VALUE_CHARACTERS");
  IElementType KEY_VALUE_SEPARATOR = new PropertiesElementType("KEY_VALUE_SEPARATOR");

  TokenSet COMMENTS = TokenSet.create(END_OF_LINE_COMMENT);
  TokenSet WHITESPACES = TokenSet.create(WHITE_SPACE);
}
public interface GroovyTokenSets {

  /**
   * http://docs.groovy-lang.org/latest/html/documentation/core-syntax.html#_keywords
   */
  TokenSet KEYWORDS = create(
    KW_AS, KW_ASSERT, KW_BREAK, KW_CASE,
    KW_CATCH, KW_CLASS, /*const,*/ KW_CONTINUE,
    KW_DEF, KW_VAR, KW_DEFAULT, KW_DO, KW_ELSE,
    KW_ENUM, KW_EXTENDS, KW_FALSE, KW_FINALLY,
    KW_FOR, /*goto,*/ KW_IF, KW_IMPLEMENTS,
    KW_IMPORT, KW_IN, T_NOT_IN, KW_INSTANCEOF, T_NOT_INSTANCEOF, KW_INTERFACE,
    KW_NEW, KW_NULL, KW_PACKAGE, KW_RETURN,
    KW_SUPER, KW_SWITCH, KW_THIS, KW_THROW,
    KW_THROWS, KW_TRAIT, KW_TRUE, KW_TRY,
    KW_WHILE, KW_YIELD
  );

  TokenSet STRING_LITERALS = create(STRING_SQ, STRING_TSQ, STRING_DQ, STRING_TDQ);

  TokenSet LOGICAL_OPERATORS = create(T_LAND, T_LOR);
  TokenSet EQUALITY_OPERATORS = create(T_EQ, T_NEQ, T_ID, T_NID);
  TokenSet RELATIONAL_OPERATORS = create(T_GT, T_GE, T_LT, T_LE, T_COMPARE);
  TokenSet BITWISE_OPERATORS = create(T_BAND, T_BOR, T_XOR);
  TokenSet ADDITIVE_OPERATORS = create(T_PLUS, T_MINUS);
  TokenSet MULTIPLICATIVE_OPERATORS = create(T_STAR, T_DIV, T_REM);
  TokenSet SHIFT_OPERATORS = create(LEFT_SHIFT_SIGN, RIGHT_SHIFT_SIGN, RIGHT_SHIFT_UNSIGNED_SIGN);
  TokenSet REGEX_OPERATORS = create(T_REGEX_FIND, T_REGEX_MATCH);
  TokenSet RANGES = create(T_RANGE, T_RANGE_BOTH_OPEN, T_RANGE_LEFT_OPEN, T_RANGE_RIGHT_OPEN);
  TokenSet OTHER_OPERATORS = create(KW_AS, KW_IN, T_NOT_IN, T_POW, KW_INSTANCEOF, T_NOT_INSTANCEOF);
  TokenSet BINARY_OPERATORS = orSet(
    LOGICAL_OPERATORS,
    EQUALITY_OPERATORS,
    RELATIONAL_OPERATORS,
    BITWISE_OPERATORS,
    ADDITIVE_OPERATORS,
    MULTIPLICATIVE_OPERATORS,
    SHIFT_OPERATORS,
    REGEX_OPERATORS,
    RANGES,
    OTHER_OPERATORS
  );

  TokenSet OPERATOR_ASSIGNMENTS = create(
    T_POW_ASSIGN,
    T_STAR_ASSIGN,
    T_DIV_ASSIGN,
    T_REM_ASSIGN,
    T_PLUS_ASSIGN,
    T_MINUS_ASSIGN,
    T_LSH_ASSIGN,
    T_RSH_ASSIGN,
    T_RSHU_ASSIGN,
    T_BAND_ASSIGN,
    T_XOR_ASSIGN,
    T_BOR_ASSIGN
  );

  TokenSet ASSIGNMENTS = orSet(
    create(T_ASSIGN, T_ELVIS_ASSIGN),
    OPERATOR_ASSIGNMENTS
  );

  TokenSet REFERENCE_DOTS = create(T_DOT, T_SAFE_DOT, T_SAFE_CHAIN_DOT, T_SPREAD_DOT);
  TokenSet METHOD_REFERENCE_DOTS = create(T_METHOD_CLOSURE, T_METHOD_REFERENCE);
  TokenSet SAFE_DOTS = create(T_SAFE_DOT, T_SAFE_CHAIN_DOT);
  TokenSet DOTS = orSet(REFERENCE_DOTS, METHOD_REFERENCE_DOTS);
}

4、嵌入式语言

        编程语言的另外一个高级特性就是可以混合其它语言一起使用。例如在某些模板语言中嵌入 Java 代码片段,这种情况中需要为可以嵌入的不同类型的片段定义chameleon类型的令牌,从ILazyParseableElementType接口实现,在解析时整个嵌入式代码片断会做过一个整体交给ILazyParseableElementType.parseContents()来进行处理。

二、实现示例

1、定义Lexer

        这个文件也不需要生动编写,使用Gramme KIT插件从.bnf文件生成即可,Simple.flex:

package org.intellij.sdk.language;

import com.intellij.lexer.FlexLexer;
import com.intellij.psi.tree.IElementType;
import org.intellij.sdk.language.psi.SimpleTypes;
import com.intellij.psi.TokenType;

%%

%class SimpleLexer
%implements FlexLexer
%unicode
%function advance
%type IElementType
%eof{  return;
%eof}

CRLF=\R
WHITE_SPACE=[\ \n\t\f]
FIRST_VALUE_CHARACTER=[^ \n\f\\] | "\\"{CRLF} | "\\".
VALUE_CHARACTER=[^\n\f\\] | "\\"{CRLF} | "\\".
END_OF_LINE_COMMENT=("#"|"!")[^\r\n]*
SEPARATOR=[:=]
KEY_CHARACTER=[^:=\ \n\t\f\\] | "\\ "

%state WAITING_VALUE

%%

<YYINITIAL> {END_OF_LINE_COMMENT}                           { yybegin(YYINITIAL); return SimpleTypes.COMMENT; }

<YYINITIAL> {KEY_CHARACTER}+                                { yybegin(YYINITIAL); return SimpleTypes.KEY; }

<YYINITIAL> {SEPARATOR}                                     { yybegin(WAITING_VALUE); return SimpleTypes.SEPARATOR; }

<WAITING_VALUE> {CRLF}({CRLF}|{WHITE_SPACE})+               { yybegin(YYINITIAL); return TokenType.WHITE_SPACE; }

<WAITING_VALUE> {WHITE_SPACE}+                              { yybegin(WAITING_VALUE); return TokenType.WHITE_SPACE; }

<WAITING_VALUE> {FIRST_VALUE_CHARACTER}{VALUE_CHARACTER}*   { yybegin(YYINITIAL); return SimpleTypes.VALUE; }

({CRLF}|{WHITE_SPACE})+                                     { yybegin(YYINITIAL); return TokenType.WHITE_SPACE; }

[^]                                                         { return TokenType.BAD_CHARACTER; }

2、生成一个 Lexer Class

        这也不需要手动编码,选择上面的Simple.flex文件,然后右键【Run JFlex Generator】,IDE 会在gen目录下生成词法分析器,例如/src /main /gen /org /intellij /sdk /language /SimpleLexer.java

 3、定义Lexer Adapter

        定义这个类的主要目的是为了适配IntelliJ Platform Lexer API。

public class SimpleLexerAdapter extends FlexAdapter {

  public SimpleLexerAdapter() {
    super(new SimpleLexer(null));
  }

}

4、定义Root File

        实现Simple语言树的顶级节点

public class SimpleFile extends PsiFileBase {

  public SimpleFile(@NotNull FileViewProvider viewProvider) {
    super(viewProvider, SimpleLanguage.INSTANCE);
  }

  @NotNull
  @Override
  public FileType getFileType() {
    return SimpleFileType.INSTANCE;
  }

  @Override
  public String toString() {
    return "Simple File";
  }

}

5、定义TokenSets

public interface SimpleTokenSets {

  TokenSet IDENTIFIERS = TokenSet.create(SimpleTypes.KEY);

  TokenSet COMMENTS = TokenSet.create(SimpleTypes.COMMENT);

}

6、定义Parser

        为避免在初始化扩展点实现时进行不必要的类加载,所有TokenSet返回值都应使用专用$Language$TokenSets类中的常量。

public class SimpleParserDefinition implements ParserDefinition {

  public static final IFileElementType FILE = new IFileElementType(SimpleLanguage.INSTANCE);

  @NotNull
  @Override
  public Lexer createLexer(Project project) {
    return new SimpleLexerAdapter();
  }

  @NotNull
  @Override
  public TokenSet getCommentTokens() {
    return SimpleTokenSets.COMMENTS;
  }

  @NotNull
  @Override
  public TokenSet getStringLiteralElements() {
    return TokenSet.EMPTY;
  }

  @NotNull
  @Override
  public PsiParser createParser(final Project project) {
    return new SimpleParser();
  }

  @NotNull
  @Override
  public IFileElementType getFileNodeType() {
    return FILE;
  }

  @NotNull
  @Override
  public PsiFile createFile(@NotNull FileViewProvider viewProvider) {
    return new SimpleFile(viewProvider);
  }

  @NotNull
  @Override
  public PsiElement createElement(ASTNode node) {
    return SimpleTypes.Factory.createElement(node);
  }

}

6、注册解析器

<extensions defaultExtensionNs="com.intellij">
  <lang.parserDefinition
      language="Simple"
      implementationClass="org.intellij.sdk.language.SimpleParserDefinition"/>
</extensions>

7、测试运行

        创建一个包含以下内容的test.simple文件

# You are reading the ".properties" entry.
! The exclamation mark can also mark text as comments.
website = https://en.wikipedia.org/
language = English
# The backslash below tells the application to continue reading
# the value onto the next line.
message = Welcome to \
          Wikipedia!
# Add spaces to the key
key\ with\ spaces = This is the value that could be looked up with the key "key with spaces".
# Unicode
tab : \u0009

        现在打开PsiViewer工具窗口并检查词法分析器如何将文件内容分解为标记,以及解析器如何将标记转换为 PSI 元素。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/793565.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

中南大学硕士论文latex版本全指导

要毕业了&#xff0c;闲下点时间写的东西。之前一直收益与师兄师姐流传下来的latex版本&#xff0c;用起来很舒服&#xff0c;希望后面的学弟学妹也能完美用上。latex功能很强大&#xff0c;不需要自己排版&#xff0c;只管内容即可&#xff0c;但是安装流程会多一丢丢。 目录 …

QT--day2(信号与槽,多界面跳转)

第一个界面头文件&#xff1a; #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QIcon> //图标头文件 #include <QPushButton> //按钮类头文件QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public…

弱电系统与IBMS系统,强强联手打造智能建筑

随着科技的飞速发展和人们对建筑设施需求的不断提升&#xff0c;智能建筑正逐渐成为建筑行业的重要发展方向。智能建筑是指通过应用先进的技术和系统&#xff0c;对建筑物的结构、系统、服务和管理等进行优化组合&#xff0c;实现建筑设施的智能化和自动化。当前&#xff0c;智…

全网低价乱价怎么解决

随着品牌的逐步发展&#xff0c;销售渠道也会渐渐增多&#xff0c;比如电商平台中的产品链接&#xff0c;会随着品牌销售店铺的增多、销售SKU的增多&#xff0c;链接量逐步上升&#xff0c;各链接的价格也会有高有低&#xff0c;有些还会低于品牌要求的建议销售价&#xff0c;所…

Java spring Aop实战

0目录 Spring AOP 1.实战 1.实战 创建工程和依赖 数据库建表 实体类 Mapper 接口 方法一 方法二 Service包 接口1&#xff1a; 实现接口 Mapper Mapper 1 Mapper 2 配置xml文件 Xml 1 Xml 2 Spring 配置文件 Mybatis配置文件 测试类 数据库结果 …

信息系统项目管理师(第四版)教材精读思维导图-第三章信息系统治理

请参阅我的另一篇文章&#xff0c;综合介绍软考高项&#xff1a; 信息系统项目管理师&#xff08;软考高项&#xff09;备考总结_计算机技术与软件专业技术_铭记北宸的博客-CSDN博客 目录 3.1 IT治理 3.2 IT审计 3.1 IT治理 3.2 IT审计

小创业公司死亡剧本

感觉蛮真实的&#xff1b;很多小创业公司没有阿里华为的命&#xff0c;却得了阿里华为的病。小的创业公司要想活无非以下几点&#xff1a; 1 现金流&#xff0c;现金流&#xff0c;现金流&#xff1b; 2 产品&#xff0c;找痛点&#xff0c;不要搞伪需求&#xff1b; 3 根据公司…

DAY2,Qt(继续完善登录框,信号与槽的使用 )

1.继续完善登录框&#xff0c;当登录成功时&#xff0c;关闭登录界面&#xff0c;跳转到新的界面中&#xff0c;来回切换页面&#xff1b; ---mychat.h chatroom.h---两个页面头文件 #ifndef MYCHAT_H #define MYCHAT_H#include <QWidget> #include <QDebug> /…

Error in onLoad hook: “ReferenceError: plus is not defined“ found in

项目场景&#xff1a; 项目背景如下所示&#xff1a; 使用 HBuilder X 开发 项目&#xff0c; 调整页面时&#xff0c;直接运行到 浏览器查看页面设置效果&#xff0c;导致控制台出现下述报错信息 例如&#xff1a; 问题描述 遇到的问题如下所示&#xff1a; APP 中接收数据…

aigo S7 PSSD 固态硬盘提示“没有初始化”的修复分享

关于固态硬盘 我们知道,固态硬盘(SSD)因其轻、薄、快、低耗电、耐震、稳定性高、耐低温等优点得到了快速的应用,在存储设备市场中占有很大份额并在持续增长。但是,我们的固态硬盘一旦损坏,数据就比较难以恢复。 今天粉丝送来的一个aigo S7 PSSD 固态硬盘进行修复,插入…

XCP详解「3.2·CANape新建工程导入A2L」

返回 XCP详解「总目录」 目录 1、新建APE工程 2、导入A2L文件 3、查看信号 4、记录数据 1、新建APE工程 打开CANape 17.0 创建新工程 创建工程名 选择保存路径 完成后&#xff0c;会开启新工程空白界面 2、导入A2L文件 导入3.1中建好的A2L文件 根据需要设置Channel&#…

第七章:WILDCAT: 弱监督学习的深度卷积神经网络用于图像分类、点位定位和分割

0.摘要 本文介绍了WILDCAT&#xff0c;一种深度学习方法&#xff0c;它旨在通过对齐图像区域来获得空间不变性和学习强烈局部化特征。我们的模型仅使用全局图像标签进行训练&#xff0c;并致力于三个主要的视觉识别任务&#xff1a;图像分类、弱监督的逐点对象定位和语义分割。…

1500多名开发者集体提起诉讼,苹果公司回应:不垄断,还提供价值

根据路透社报道&#xff0c;1500多名开发者集体提起诉讼&#xff0c;指控苹果公司在其App Store上存在不公平行为并征收高额佣金。该诉讼要求赔偿高达7.85亿英镑&#xff08;约合72.38亿元人民币&#xff09;。 根据9to5Mac报道&#xff0c;苹果公司对最新起诉提出回应&#xf…

优秀的帮助中心可不能缺少这些元素

随着服务行业越来越卷&#xff0c;很多企业都不满足于只用人工客服来宣传自己的产品和维持售后服务&#xff0c;更多企业开始把目光投向了在线的帮助中心。不仅可以解决人工成本高的问题&#xff0c;还可以实现24小时的全天候服务。是一个绝佳的选择。 优秀的帮助中心必备元素 …

fastadmin appendfieldlist 用法

先上效果 字段添加 ALTER TABLE ewg1990.fa_quanzi ADD COLUMN yiqi_images text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT 厂家赞助 AFTER yiqi_images; 添加页面 <!--赞助商--><div class"form-group row"><!-- &…

有关 openAPI 的一些总结

目前主流的 APi 的验证是&#xff1a;Tokensign sign 主要是保证数据的真实性 token 主要是进行接口安全访问的 sign验证签名&#xff08; sha256Hex&#xff09; 一般将一些平台的版本及平台 id 等字段进行固定拼接后再进行摘要算法处理 // 参与签名计算的Key-Value列表 Map…

【青书学堂】计算机组装与维护(直播课) 第一学期 考试

【青书学堂】计算机组装与维护(直播课) 第一学期 考试 标题最终成绩:91.15 分 注意:答案仅供参考 第1题 单选题 第三代计算机的主要元件是:()。 A: 电子管 B: 中、小规模集成电路 C: 晶体管 D: 大规模和超大规模集成电路 答案:B 第2题 单选题 下列哪项属于网络设备()…

雷达信号处理自学总结(持续更新)

傅里叶变换的频率分辨率 频率分辨率 采样频率 信号长度 频率分辨率 \frac{采样频率 }{信号长度} 频率分辨率信号长度采样频率​ 可用numpy模块的fft.fftfreq函数求出傅里叶变换的频率分辨率。 https://numpy.org/doc/stable/reference/generated/numpy.fft.fftfreq.html

2.1 色彩空间

色彩发送器 色彩认知&#xff1a;光源是出生点&#xff0c;光源发射出光线&#xff0c;光线通过直射反射折射等路径最终进入人眼。但是人眼接收到光线后&#xff0c;人眼的细胞产生了一系列化学反应。由此把产生的新号传入大脑&#xff0c;最终大脑对颜色产生了认知感知。 光的…

Nacos开启权限认证

1.修改Nacos配置 配置文件在nacos目录的conf下 修改点&#xff1a; 注意&#xff1a;需要添加对应的数据库nacos&#xff0c;对应的SQL文件也是在conf目录下 2.修改bootstrap.yaml配置文件 spring:application:name: servernameprofiles:active: devcloud:nacos:username: na…