0

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

柔性:

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; }

恩夫:

{
  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 是递归的。如何将其重写为不递归?其次,当我尝试编译并运行它时,它在尝试解析此语法时冻结了idea测试实例,看起来像一个无限循环。

4

1 回答 1

2

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

由于底层技术是 PEG,所以不能使用左递归规则。左递归将导致解析器中的无限循环,除非代码生成器拒绝生成左递归代码。幸运的是,可以使用重复运算符,因此您只需要对涉及括号的语法进行递归,而这不是左递归,因此没有问题。

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

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

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

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

于 2019-05-16T20:26:58.717 回答