Need help with grammar-kit for left recurrsion
Answered
I would like some help getting my BNF working to handle left recursion for a plugin I'm writing. The plugin is for the HashiCorp Sentinel language which is based on a mix of Go and Python. Here is the language spec: https://docs.hashicorp.com/sentinel/language/spec/
I'm stuck on how to handle left recursion for "PrimaryExpression" and I don't know how to fix it. I was able to make grammar-kit happy with "Expression". I think the problem is that not all the child rules completely inherit from the parent but I don't know how to make that happen. Below a the partial BNF file.
{
parserClass="com.github.tylersmith34.intellijhashicorpsentinel.SentinelParser"
parserUtilClass="com.github.tylersmith34.intellijhashicorpsentinel.SentinelParserUtil"
extends="com.intellij.extapi.psi.ASTWrapperPsiElement"
psiClassPrefix="Sentinel"
psiImplClassSuffix="Impl"
psiPackage="com.github.tylersmith34.intellijhashicorpsentinel.psi"
psiImplPackage="com.github.tylersmith34.intellijhashicorpsentinel.psi.impl"
elementTypeHolderClass="com.github.tylersmith34.intellijhashicorpsentinel.SentinelTypes"
elementTypeClass="com.github.tylersmith34.intellijhashicorpsentinel.psi.SentinelElementType"
tokenTypeClass="com.github.tylersmith34.intellijhashicorpsentinel.psi.SentinelTokenType"
psiImplUtilClass="com.github.tylersmith34.intellijhashicorpsentinel.psi.impl.SentinelPsiImplUtil"
tokens = [
IDENTIFIER = 'regexp:[a-zA-Z_][a-zA-Z0-9_]*'
WHITE_SPACE = 'regexp:\s+'
DOUBLE_QUOTED_STRING="regexp:\"([^\\\"\r\n]|\\[^\r\n])*\"?"
SINGLE_QUOTED_STRING="regexp:'([^\\\'\r\n]|\\[^\r\n])*'?"
COMMENT = "regexp:(//.*)|(#.*)"
BLOCK_COMMENT = "regexp:/\*([^*]|\*[^/])*\*?(\*/)?"
NUMBERS='regexp:-?(0x)?(0|[1-9])\d*(\.\d+)?([eE][-+]?\d+)?'
L_BRACKET='['
R_BRACKET=']'
L_CURLY='{'
R_CURLY='}'
L_PAREN='('
R_PAREN=')'
COMMA=','
PERIOD='.'
EQUALS='='
LESS_THAN='<'
MINUS='-'
PLUS='+'
SPLAT='*'
SLASH='/'
PERCENT='%'
COLON=':'
DOUBLE_EQUALS='=='
NOT_EQUALS='!='
LESS_THAN_EQUALS='<='
GREATER_THAN='>'
GREATER_THAN_EQUALS='>='
MINUS='-'
EXCLAMATION='!'
]
}
//SimpleFile ::= IfStatement*
SimpleFile ::= Definition*
Definition ::= { ImportStatement | ExternalParameters | Variable}
//Complex Objects
Object ::= String | Constants
String ::= ('"' ( Letter | EscapedCharacter )* '"') | DOUBLE_QUOTED_STRING | SINGLE_QUOTED_STRING
Variable ::= IDENTIFIER EQUALS ExpressionList
//import
ImportStatement ::= import String (as IDENTIFIER )?
//External param
ExternalParameters ::= param IDENTIFIER ( default Object )?
//Primitive types
Constants ::= BooleanLiteral | NullLiteral | UndefinedLiteral
BooleanLiteral ::= true | false
NullLiteral ::= null
UndefinedLiteral ::= undefined
Letter ::= "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z" | "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z"
EscapedCharacter ::= '\' ( "a" | "b" | "f" | "n" | "r" | "t" | "v" | '\' | '"' )
ExpressionList ::= Expression ("," Expression )*
Expression ::= Expr
Expr ::= UnaryExpr | ExprWithExpr
ExprWithExpr ::= Expr binary_op Expr {extends=Expr}
UnaryExpr ::= PrimaryExpression | unary_op UnaryExpr {extends=Expr}
binary_op ::= logical_op | set_op | rel_op | add_op | mul_op | else_op
logical_op ::= "and" | "or" | "xor"
set_op ::= ("not")? ( "contains" | "in" )
rel_op ::= "==" | "!=" | "<" | "<=" | ">" | ">=" | "is" | "is not" | "matches" | "not matches"
add_op ::= "+" | "-"
mul_op ::= "*" | "/" | "%"
unary_op ::= "+" | "-" | "!" | "not"
else_op ::= else
PrimaryExpression ::= Operand | PrimarySelectorExpr | PrimaryIndexExpr | PrimarySliceExpr | PrimaryArgumentsExpr
private PrimaryArgumentsExpr ::= PrimaryExpression Arguments {extends=PrimaryExpression}
private PrimarySliceExpr ::= PrimaryExpression Slice {extends=PrimaryExpression}
private PrimaryIndexExpr ::= PrimaryExpression Index {extends=PrimaryExpression}
private PrimarySelectorExpr ::= PrimaryExpression Selector {extends=PrimaryExpression}
Selector ::= "." IDENTIFIER {extends=PrimaryExpression}
Index ::= "[" Expression "]" {extends=PrimaryExpression}
Slice ::= "[" [ Expression ] ":" [ Expression ] "]" | "[" [ Expression ] ":" Expression ":" Expression "]" {extends=PrimaryExpression}
Arguments ::= "(" [ ( ExpressionList ) [ "..." ] [ "," ] ] ")" {extends=PrimaryExpression}
Operand ::= Literal | OperandName | "(" Expression ")" {extends=PrimaryExpression}
Literal ::= BasicLit
BasicLit ::= String
OperandName ::= IDENTIFIER
Please sign in to leave a comment.
Sorry, this thread got lost. There's a dedicated section on how to implement Expression parsing here https://github.com/JetBrains/Grammar-Kit/blob/master/HOWTO.md#24-compact-expression-parsing-with-priorities, HTH.