From 98e9ac6279ead2c81511a85fd93a750e5552aa0c Mon Sep 17 00:00:00 2001 From: Yann D'Isanto Date: Fri, 1 Sep 2023 14:15:34 +0200 Subject: [PATCH] parser and lexer --- .gitignore | 2 +- build.gradle.kts | 37 +++++++++- .../le_yams/openfga4intellij/OpenFGAFile.java | 25 +++++++ .../openfga4intellij/OpenFGAFileType.java | 2 +- .../openfga4intellij/OpenFGAIcons.java | 2 +- .../openfga4intellij/OpenFGALanguage.java | 2 +- .../openfga4intellij/OpenFGALexerAdapter.java | 12 ++++ .../parsing}/OpenFGAElementType.java | 4 +- .../parsing/OpenFGALexer.flex | 63 ++++++++++++++++ .../parsing/OpenFGAParserDefinition.java | 71 +++++++++++++++++++ .../parsing/OpenFGATokenSets.java | 22 ++++++ .../parsing}/OpenFGATokenType.java | 4 +- .../openfga4intellij/parsing}/openfga.bnf | 56 +++++++-------- src/main/resources/META-INF/plugin.xml | 5 +- 14 files changed, 266 insertions(+), 41 deletions(-) create mode 100644 src/main/java/com/github/le_yams/openfga4intellij/OpenFGAFile.java rename src/main/java/com/{example => github/le_yams}/openfga4intellij/OpenFGAFileType.java (94%) rename src/main/java/com/{example => github/le_yams}/openfga4intellij/OpenFGAIcons.java (82%) rename src/main/java/com/{example => github/le_yams}/openfga4intellij/OpenFGALanguage.java (83%) create mode 100644 src/main/java/com/github/le_yams/openfga4intellij/OpenFGALexerAdapter.java rename src/main/java/com/{example/openfga4intellij/psi => github/le_yams/openfga4intellij/parsing}/OpenFGAElementType.java (69%) create mode 100644 src/main/java/com/github/le_yams/openfga4intellij/parsing/OpenFGALexer.flex create mode 100644 src/main/java/com/github/le_yams/openfga4intellij/parsing/OpenFGAParserDefinition.java create mode 100644 src/main/java/com/github/le_yams/openfga4intellij/parsing/OpenFGATokenSets.java rename src/main/java/com/{example/openfga4intellij/psi => github/le_yams/openfga4intellij/parsing}/OpenFGATokenType.java (78%) rename src/main/java/com/{example/openfga4intellij => github/le_yams/openfga4intellij/parsing}/openfga.bnf (55%) diff --git a/.gitignore b/.gitignore index 7028283..02ae2b9 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,7 @@ build/ *.iml *.ipr out/ -**/src/main/**/gen/ +**/src/generated/ !**/src/main/**/out/ !**/src/test/**/out/ diff --git a/build.gradle.kts b/build.gradle.kts index b32b8f9..01ae5d1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,17 +1,20 @@ + plugins { id("java") id("org.jetbrains.kotlin.jvm") version "1.9.0" id("org.jetbrains.intellij") version "1.15.0" + id("org.jetbrains.grammarkit") version "2022.3.1" } -group = "com.example" +group = "com.github.le_yams" version = "1.0-SNAPSHOT" repositories { mavenCentral() } -sourceSets["main"].java.srcDirs("src/main/gen") +sourceSets["main"].java.srcDirs("src/generated/java", "src/main/java") + // Configure Gradle IntelliJ Plugin // Read more: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html @@ -22,7 +25,22 @@ intellij { plugins.set(listOf(/* Plugin Dependencies */)) } +grammarKit { + jflexRelease.set("1.7.0-1") + grammarKitRelease.set("2021.1.2") + intellijRelease.set("203.7717.81") +} + tasks { + + compileJava { + + dependsOn( + generateLexer, + generateParser, + ) + } + // Set the JVM compatibility versions withType { sourceCompatibility = "17" @@ -32,6 +50,21 @@ tasks { kotlinOptions.jvmTarget = "17" } + generateLexer { + sourceFile.set(file("src/main/java/com/github/le_yams/openfga4intellij/parsing/OpenFGALexer.flex")) + targetDir.set("src/generated/java/com/github/le_yams/openfga4intellij/parsing") + targetClass.set("OpenFGALexer") + purgeOldFiles.set(true) + } + + generateParser { + sourceFile.set(file("src/main/java/com/github/le_yams/openfga4intellij/parsing/openfga.bnf")) + targetRoot.set("src/generated/java") + pathToParser.set("com/github/le_yams/openfga4intellij/parsing/RustParser.java") + pathToPsiRoot.set("com/github/le_yams/openfga4intellij/psi") + purgeOldFiles.set(true) + } + patchPluginXml { sinceBuild.set("222") untilBuild.set("232.*") diff --git a/src/main/java/com/github/le_yams/openfga4intellij/OpenFGAFile.java b/src/main/java/com/github/le_yams/openfga4intellij/OpenFGAFile.java new file mode 100644 index 0000000..2021501 --- /dev/null +++ b/src/main/java/com/github/le_yams/openfga4intellij/OpenFGAFile.java @@ -0,0 +1,25 @@ +package com.github.le_yams.openfga4intellij; + +import com.intellij.extapi.psi.PsiFileBase; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.psi.FileViewProvider; +import org.jetbrains.annotations.NotNull; + +public class OpenFGAFile extends PsiFileBase { + + public OpenFGAFile(@NotNull FileViewProvider viewProvider) { + super(viewProvider, OpenFGALanguage.INSTANCE); + } + + @NotNull + @Override + public FileType getFileType() { + return OpenFGAFileType.INSTANCE; + } + + @Override + public String toString() { + return getFileType().getName(); + } + +} diff --git a/src/main/java/com/example/openfga4intellij/OpenFGAFileType.java b/src/main/java/com/github/le_yams/openfga4intellij/OpenFGAFileType.java similarity index 94% rename from src/main/java/com/example/openfga4intellij/OpenFGAFileType.java rename to src/main/java/com/github/le_yams/openfga4intellij/OpenFGAFileType.java index 25ace2a..453babd 100644 --- a/src/main/java/com/example/openfga4intellij/OpenFGAFileType.java +++ b/src/main/java/com/github/le_yams/openfga4intellij/OpenFGAFileType.java @@ -1,4 +1,4 @@ -package com.example.openfga4intellij; +package com.github.le_yams.openfga4intellij; import com.intellij.openapi.fileTypes.LanguageFileType; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/com/example/openfga4intellij/OpenFGAIcons.java b/src/main/java/com/github/le_yams/openfga4intellij/OpenFGAIcons.java similarity index 82% rename from src/main/java/com/example/openfga4intellij/OpenFGAIcons.java rename to src/main/java/com/github/le_yams/openfga4intellij/OpenFGAIcons.java index d1ccd1a..0641d3d 100644 --- a/src/main/java/com/example/openfga4intellij/OpenFGAIcons.java +++ b/src/main/java/com/github/le_yams/openfga4intellij/OpenFGAIcons.java @@ -1,4 +1,4 @@ -package com.example.openfga4intellij; +package com.github.le_yams.openfga4intellij; import com.intellij.openapi.util.IconLoader; diff --git a/src/main/java/com/example/openfga4intellij/OpenFGALanguage.java b/src/main/java/com/github/le_yams/openfga4intellij/OpenFGALanguage.java similarity index 83% rename from src/main/java/com/example/openfga4intellij/OpenFGALanguage.java rename to src/main/java/com/github/le_yams/openfga4intellij/OpenFGALanguage.java index f36ecef..75fc1a3 100644 --- a/src/main/java/com/example/openfga4intellij/OpenFGALanguage.java +++ b/src/main/java/com/github/le_yams/openfga4intellij/OpenFGALanguage.java @@ -1,4 +1,4 @@ -package com.example.openfga4intellij; +package com.github.le_yams.openfga4intellij; import com.intellij.lang.Language; diff --git a/src/main/java/com/github/le_yams/openfga4intellij/OpenFGALexerAdapter.java b/src/main/java/com/github/le_yams/openfga4intellij/OpenFGALexerAdapter.java new file mode 100644 index 0000000..fd95fc4 --- /dev/null +++ b/src/main/java/com/github/le_yams/openfga4intellij/OpenFGALexerAdapter.java @@ -0,0 +1,12 @@ +package com.github.le_yams.openfga4intellij; + +import com.github.le_yams.openfga4intellij.parsing.OpenFGALexer; +import com.intellij.lexer.FlexAdapter; + +public class OpenFGALexerAdapter extends FlexAdapter { + + public OpenFGALexerAdapter() { + super(new OpenFGALexer(null)); + } + +} \ No newline at end of file diff --git a/src/main/java/com/example/openfga4intellij/psi/OpenFGAElementType.java b/src/main/java/com/github/le_yams/openfga4intellij/parsing/OpenFGAElementType.java similarity index 69% rename from src/main/java/com/example/openfga4intellij/psi/OpenFGAElementType.java rename to src/main/java/com/github/le_yams/openfga4intellij/parsing/OpenFGAElementType.java index a40a15c..a20b693 100644 --- a/src/main/java/com/example/openfga4intellij/psi/OpenFGAElementType.java +++ b/src/main/java/com/github/le_yams/openfga4intellij/parsing/OpenFGAElementType.java @@ -1,6 +1,6 @@ -package com.example.openfga4intellij.psi; +package com.github.le_yams.openfga4intellij.parsing; -import com.example.openfga4intellij.OpenFGALanguage; +import com.github.le_yams.openfga4intellij.OpenFGALanguage; import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.NonNls; diff --git a/src/main/java/com/github/le_yams/openfga4intellij/parsing/OpenFGALexer.flex b/src/main/java/com/github/le_yams/openfga4intellij/parsing/OpenFGALexer.flex new file mode 100644 index 0000000..cecc419 --- /dev/null +++ b/src/main/java/com/github/le_yams/openfga4intellij/parsing/OpenFGALexer.flex @@ -0,0 +1,63 @@ +package com.github.le_yams.openfga4intellij.parsing; + +import com.intellij.lexer.FlexLexer; +import com.intellij.psi.tree.IElementType; + +import static com.intellij.psi.TokenType.BAD_CHARACTER; +import static com.intellij.psi.TokenType.WHITE_SPACE; +import static com.github.le_yams.openfga4intellij.psi.OpenFGATypes.*; + +%% + +%{ + public OpenFGALexer() { + this((java.io.Reader)null); + } +%} + +%public +%class OpenFGALexer +%implements FlexLexer +%function advance +%type IElementType +%unicode + +ALPHA_NUMERIC=[a-zA-Z0-9_-]+ +END_OF_LINE=(\r\n)|\n +WHITESPACE=[\ \t] +IDENT1=((\ {2})|\t) +IDENT2=((\ {4})|(\t{2})) +SINGLE_LINE_COMMENT=[ \t]*#.* + +%% + { + " " { return WHITE_SPACE; } + + "model" { return MODEL; } + "schema" { return SCHEMA; } + "1.1" { return SCHEMA_VERSION_V1_1; } + "type" { return TYPE; } + "relations" { return RELATIONS; } + "define" { return DEFINE; } + "#" { return HASH; } + ":" { return COLON; } + "*" { return WILDCARD; } + "[" { return L_SQUARE; } + "]" { return R_SQUARE; } + "," { return COMMA; } + "and" { return AND; } + "or" { return OR; } + "but not" { return BUT_NOT; } + "from" { return FROM; } + "EOF" { return EOF; } + + {ALPHA_NUMERIC} { return ALPHA_NUMERIC; } + {END_OF_LINE} { return END_OF_LINE; } + {WHITESPACE} { return WHITESPACE; } + {IDENT1} { return IDENT1; } + {IDENT2} { return IDENT2; } + {SINGLE_LINE_COMMENT} { return SINGLE_LINE_COMMENT; } + +} + +[^] { return BAD_CHARACTER; } diff --git a/src/main/java/com/github/le_yams/openfga4intellij/parsing/OpenFGAParserDefinition.java b/src/main/java/com/github/le_yams/openfga4intellij/parsing/OpenFGAParserDefinition.java new file mode 100644 index 0000000..e479d8d --- /dev/null +++ b/src/main/java/com/github/le_yams/openfga4intellij/parsing/OpenFGAParserDefinition.java @@ -0,0 +1,71 @@ +package com.github.le_yams.openfga4intellij.parsing; + +import com.github.le_yams.openfga4intellij.OpenFGAFile; +import com.github.le_yams.openfga4intellij.OpenFGALanguage; +import com.github.le_yams.openfga4intellij.OpenFGALexerAdapter; +import com.github.le_yams.openfga4intellij.psi.OpenFGATypes; +import com.intellij.lang.ASTNode; +import com.intellij.lang.ParserDefinition; +import com.intellij.lang.PsiParser; +import com.intellij.lexer.Lexer; +import com.intellij.openapi.project.Project; +import com.intellij.psi.FileViewProvider; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.tree.IFileElementType; +import com.intellij.psi.tree.TokenSet; +import org.jetbrains.annotations.NotNull; + +public class OpenFGAParserDefinition implements ParserDefinition { + + public static final IFileElementType FILE = new IFileElementType(OpenFGALanguage.INSTANCE); + + @NotNull + @Override + public Lexer createLexer(Project project) { + return new OpenFGALexerAdapter(); + } + + @NotNull + @Override + public TokenSet getCommentTokens() { + return OpenFGATokenSets.SINGLE_LINE_COMMENT; + } + + @NotNull + @Override + public TokenSet getStringLiteralElements() { + return TokenSet.EMPTY; + } + +// @NotNull +// @Override +// public TokenSet getWhitespaceTokens() { +// return OpenFGATokenSets.WHITE_SPACE; +// } + + @NotNull + @Override + public PsiParser createParser(final Project project) { + return new OpenFGAParser(); + } + + @NotNull + @Override + public IFileElementType getFileNodeType() { + return FILE; + } + + @NotNull + @Override + public PsiFile createFile(@NotNull FileViewProvider viewProvider) { + return new OpenFGAFile(viewProvider); + } + + @NotNull + @Override + public PsiElement createElement(ASTNode node) { + return OpenFGATypes.Factory.createElement(node); + } + +} \ No newline at end of file diff --git a/src/main/java/com/github/le_yams/openfga4intellij/parsing/OpenFGATokenSets.java b/src/main/java/com/github/le_yams/openfga4intellij/parsing/OpenFGATokenSets.java new file mode 100644 index 0000000..8cdaa5e --- /dev/null +++ b/src/main/java/com/github/le_yams/openfga4intellij/parsing/OpenFGATokenSets.java @@ -0,0 +1,22 @@ +package com.github.le_yams.openfga4intellij.parsing; + +import com.github.le_yams.openfga4intellij.psi.OpenFGATypes; +import com.intellij.psi.tree.TokenSet; + +public interface OpenFGATokenSets { + + TokenSet KEYWORDS = TokenSet.create( + OpenFGATypes.MODEL, + OpenFGATypes.SCHEMA, + OpenFGATypes.TYPE, + OpenFGATypes.RELATIONS, + OpenFGATypes.DEFINE, + OpenFGATypes.OR, + OpenFGATypes.FROM + ); + + TokenSet RELATION_NAME = TokenSet.create(OpenFGATypes.RELATION_NAME); + TokenSet TYPE_IDENTIFIER = TokenSet.create(OpenFGATypes.TYPE_IDENTIFIER); + TokenSet SINGLE_LINE_COMMENT = TokenSet.create(OpenFGATypes.COMMENT); +// TokenSet DUMMY_WHITE_SPACE = TokenSet.create(OpenFGATypes.DUMMY_WHITE_SPACE); +} diff --git a/src/main/java/com/example/openfga4intellij/psi/OpenFGATokenType.java b/src/main/java/com/github/le_yams/openfga4intellij/parsing/OpenFGATokenType.java similarity index 78% rename from src/main/java/com/example/openfga4intellij/psi/OpenFGATokenType.java rename to src/main/java/com/github/le_yams/openfga4intellij/parsing/OpenFGATokenType.java index 9fa3dcc..c611e21 100644 --- a/src/main/java/com/example/openfga4intellij/psi/OpenFGATokenType.java +++ b/src/main/java/com/github/le_yams/openfga4intellij/parsing/OpenFGATokenType.java @@ -1,6 +1,6 @@ -package com.example.openfga4intellij.psi; +package com.github.le_yams.openfga4intellij.parsing; -import com.example.openfga4intellij.OpenFGALanguage; +import com.github.le_yams.openfga4intellij.OpenFGALanguage; import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/com/example/openfga4intellij/openfga.bnf b/src/main/java/com/github/le_yams/openfga4intellij/parsing/openfga.bnf similarity index 55% rename from src/main/java/com/example/openfga4intellij/openfga.bnf rename to src/main/java/com/github/le_yams/openfga4intellij/parsing/openfga.bnf index 30720ea..4a9b8bf 100644 --- a/src/main/java/com/example/openfga4intellij/openfga.bnf +++ b/src/main/java/com/github/le_yams/openfga4intellij/parsing/openfga.bnf @@ -1,18 +1,16 @@ { - parserClass="com.example.openfga4intellij.parser.OpenFGAParser" - - extends="com.intellij.extapi.psi.ASTWrapperPsiElement" + parserClass="com.github.le_yams.openfga4intellij.parsing.OpenFGAParser" psiClassPrefix="OpenFGA" psiImplClassSuffix="Impl" - psiPackage="com.example.openfga4intellij.psi" - psiImplPackage="com.example.openfga4intellij.psi.impl" + psiPackage="com.github.le_yams.openfga4intellij.psi" + psiImplPackage="com.github.le_yams.openfga4intellij.psi.impl" - elementTypeHolderClass="com.example.openfga4intellij.psi.OpenFGATypes" - elementTypeClass="com.example.openfga4intellij.psi.OpenFGAElementType" - tokenTypeClass="com.example.openfga4intellij.psi.OpenFGATokenType" + elementTypeHolderClass="com.github.le_yams.openfga4intellij.psi.OpenFGATypes" + elementTypeClass="com.github.le_yams.openfga4intellij.parsing.OpenFGAElementType" + tokenTypeClass="com.github.le_yams.openfga4intellij.parsing.OpenFGATokenType" tokens = [ MODEL = "model"; @@ -23,7 +21,7 @@ DEFINE = "define"; HASH = "#"; - COLON = " ="; + COLON = ":"; WILDCARD = "*"; L_SQUARE = "["; R_SQUARE = "]"; @@ -36,35 +34,30 @@ ALPHA_NUMERIC = "regexp:[a-zA-Z0-9_-]+"; - END_OF_LINE = "regexp:[\r\n]"; - WS = "regexp:[ \t]"; + END_OF_LINE = "regexp:(\r\n)|\n"; + WHITESPACE = "regexp:[\ \t]"; + IDENT1= "regexp:((\ {2})|\t)"; + IDENT2= "regexp:((\ {4})|(\t{2}))"; SINGLE_LINE_COMMENT = "regexp:[ \t]*#.*" ] } - openfgaFile ::= main main ::= modelHeader typeDefs newline? EOF -indentation ::= indent -modelHeader ::= (multiLineComment newline)? MODEL spacing? (newline multiLineComment)? indentation SCHEMA spacing schemaVersion spacing? +modelHeader ::= headerMultiLineComment? MODEL spacing? multiLineComment? indentedLine1 SCHEMA spacing schemaVersion spacing? typeDefs ::= typeDef* -typeDef ::= (newline multiLineComment)? newline TYPE spacing typeIdentifier spacing? (indentation RELATIONS spacing? relationDeclaration+)? -relationDeclaration ::= (newline multiLineComment)? indentation DEFINE spacing relationName spacing? COLON spacing? relationDef spacing? +typeDef ::= multiLineComment? newline TYPE spacing typeIdentifier spacing? (indentedLine1 RELATIONS spacing? relationDeclaration+)? +relationDeclaration ::= multiLineComment? indentedLine2 DEFINE spacing relationName spacing? COLON spacing? relationDef spacing? relationDef ::= (relationDefDirectAssignment | relationDefGrouping) relationDefPartials? relationDefPartials ::= relationDefPartialAllOr | relationDefPartialAllAnd | relationDefPartialAllButNot -relationDefPartialAllOr ::= (spacing relationDefOperatorOr spacing relationDefGrouping)+ -relationDefPartialAllAnd ::= (spacing relationDefOperatorAnd spacing relationDefGrouping)+ -relationDefPartialAllButNot ::= (spacing relationDefOperatorButNot spacing relationDefGrouping)+ +relationDefPartialAllOr ::= (spacing OR spacing relationDefGrouping)+ +relationDefPartialAllAnd ::= (spacing AND spacing relationDefGrouping)+ +relationDefPartialAllButNot ::= (spacing BUT_NOT spacing relationDefGrouping)+ relationDefDirectAssignment ::= L_SQUARE spacing? relationDefTypeRestriction spacing? (COMMA spacing? relationDefTypeRestriction)* spacing? R_SQUARE relationDefRewrite ::= relationDefRelationOnSameObject | relationDefRelationOnRelatedObject relationDefRelationOnSameObject ::= rewriteComputedusersetName -relationDefRelationOnRelatedObject ::= rewriteTuplesetComputedusersetName spacing relationDefKeywordFrom spacing rewriteTuplesetName -relationDefOperator ::= relationDefOperatorOr | relationDefOperatorAnd | relationDefOperatorButNot -relationDefOperatorAnd ::= AND -relationDefOperatorOr ::= OR -relationDefOperatorButNot ::= BUT_NOT -relationDefKeywordFrom ::= FROM +relationDefRelationOnRelatedObject ::= rewriteTuplesetComputedusersetName spacing FROM spacing rewriteTuplesetName relationDefTypeRestriction ::= relationDefTypeRestrictionType | relationDefTypeRestrictionWildcard | relationDefTypeRestrictionUserset relationDefTypeRestrictionType ::= identifier relationDefTypeRestrictionRelation ::= identifier @@ -76,9 +69,12 @@ rewriteTuplesetComputedusersetName ::= identifier rewriteTuplesetName ::= identifier relationName ::= identifier typeIdentifier ::= identifier -comment ::= SINGLE_LINE_COMMENT -multiLineComment ::= comment (newline comment)* -spacing ::= WS+ -newline ::= END_OF_LINE+ -schemaVersion ::= SCHEMA_VERSION_V1_1 identifier ::= ALPHA_NUMERIC+ +schemaVersion ::= SCHEMA_VERSION_V1_1 +spacing ::= WHITESPACE+ +indentedLine1 ::= newline IDENT1 +indentedLine2 ::= newline IDENT2 +newline ::= END_OF_LINE+ +comment ::= SINGLE_LINE_COMMENT +headerMultiLineComment ::= comment (newline comment)* +multiLineComment ::= newline comment (newline comment)* diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 00ca888..d9c76ec 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -27,9 +27,12 @@ + \ No newline at end of file