如何为IntelliJ进行简单的计算器语法突出显示?

时间:2019-05-16 18:09:26

标签: intellij-idea flex-lexer intellij-plugin bnf grammar-kit

我正在根据this教程制作自定义语言支持插件,但我坚持了一些.bnf概念。假设我想解析一种支持+,-,*,/,一元-和括号的简单计算器语言。这是我目前拥有的:

Flex:

package com.intellij.circom;

import com.intellij.lexer.FlexLexer;
import com.intellij.psi.tree.IElementType;
import com.intellij.circom.psi.CircomTypes;
import com.intellij.psi.TokenType;

%%

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

WHITESPACE = [ \n\r\t]+
NUMBER = [0-9]+

%%

{WHITESPACE}    { return TokenType.WHITE_SPACE; }
{NUMBER}        { return CircomTypes.NUMBER; }

Bnf:

{
  parserClass="com.intellij.circom.parser.CircomParser"

  extends="com.intellij.extapi.psi.ASTWrapperPsiElement"

  psiClassPrefix="Circom"
  psiImplClassSuffix="Impl"
  psiPackage="com.intellij.circom.psi"
  psiImplPackage="com.intellij.circom.psi.impl"

  elementTypeHolderClass="com.intellij.circom.psi.CircomTypes"
  elementTypeClass="com.intellij.circom.psi.CircomElementType"
  tokenTypeClass="com.intellij.circom.psi.CircomTokenType"
}

expr ::=
   expr ('+' | '-') expr
  | expr ('*' | '/') expr
  | '-' expr
  | '(' expr ')'
  | literal;
literal ::= NUMBER;

首先,它抱怨expr是递归的。我如何重写它而不是递归的?其次,当我尝试编译并运行它时,它在尝试解析此语法时冻结了想法测试实例,看起来像是一个无休止的循环。

1 个答案:

答案 0 :(得分:2)

将语法文件称为“ BNF”有点误导,因为它们实际上是经过修改的PEG(解析表达式语法)格式,该格式允许某些扩展的运算符,包括分组,重复和可选性以及有序选择(在语义上与|的常规定义。

由于底层技术是PEG,因此您不能使用左递归规则。除非代码生成器拒绝生成左递归代码,否则左递归将在解析器中引起无限循环。幸运的是,可以使用重复运算符,因此您只需要对包含括号的语法进行递归操作,而不必进行左递归操作,因此不会出现任何问题。

据我从发现的文档中可以看到,语法工具包不提供运算符优先级声明。如果确实需要考虑运算符优先级来生成正确的解析,则需要使用多个优先级。但是,如果您唯一的用例是语法突出显示,则可能不需要精确准确的语法分析,那么执行以下操作就足够了:

expr  ::= unary (('+' | '-' | '*' | '/') unary)*
unary ::= '-'* ( '(' expr ')' | literal )

(对于精确解析,您需要将上面的expr分成两个优先级,一个优先级用于加法运算符,另一个优先级用于乘法。但是我建议您不要这样做,除非您打算将解析用于评估或代码生成。)

此外,您几乎可以肯定需要一些词汇规则来识别各种运算符并返回适当的单字符标记。