Skip to content

Commit 2aea320

Browse files
committed
Added Expression Language support
1 parent 5d6fd5d commit 2aea320

16 files changed

+555
-1
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ out/
33
/.idea/misc.xml
44
/.gradle
55
/build
6+
/src/main/gen

build.gradle

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
2+
import org.jetbrains.grammarkit.tasks.*
23

34
plugins {
45
id "org.jetbrains.intellij" version "0.4.11"
56
id 'com.palantir.git-version' version "0.11.0"
7+
id "org.jetbrains.grammarkit" version "2021.1.3"
68
}
79

810
def htmlFixer = { htmlFile -> file(htmlFile).text.replace('<html>', '').replace('</html>', '') }
@@ -14,6 +16,7 @@ sourceCompatibility = JavaVersion.VERSION_11
1416
targetCompatibility = JavaVersion.VERSION_11
1517

1618
apply plugin: 'org.jetbrains.intellij'
19+
apply plugin: 'org.jetbrains.grammarkit'
1720

1821
intellij {
1922
version ideaVersion
@@ -53,6 +56,24 @@ publishPlugin {
5356
token System.getenv('IJ_TOKEN')
5457
}
5558

59+
task generateExpressionLanguageLexer(type: GenerateLexer) {
60+
source = 'src/main/java/fr/adrienbrault/idea/symfony2plugin/expressionLanguage/ExpressionLanguage.flex'
61+
targetDir = 'src/main/gen/fr/adrienbrault/idea/symfony2plugin/expressionLanguage/'
62+
targetClass = 'ExpressionLanguageLexer'
63+
}
64+
65+
task generateExpressionLanguageParser(type: GenerateParser) {
66+
source = 'src/main/java/fr/adrienbrault/idea/symfony2plugin/expressionLanguage/ExpressionLanguage.bnf'
67+
targetRoot = 'src/main/gen'
68+
pathToParser = 'src/main/gen/fr/adrienbrault/idea/symfony2plugin/expressionLanguage/ExpressionLanguageParser.java'
69+
pathToPsiRoot = 'src/main/gen/fr/adrienbrault/idea/symfony2plugin/expressionLanguage/psi'
70+
}
71+
72+
compileJava {
73+
dependsOn generateExpressionLanguageLexer
74+
dependsOn generateExpressionLanguageParser
75+
}
76+
5677
group 'fr.adrienbrault.idea.symfony2plugin'
5778

5879
def details = versionDetails()
@@ -67,3 +88,6 @@ wrapper {
6788
}
6889

6990
test.testLogging.exceptionFormat = TestExceptionFormat.FULL
91+
92+
// Include the generated files in the source set
93+
sourceSets.main.java.srcDirs 'src/main/gen'
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
{
2+
parserClass="fr.adrienbrault.idea.symfony2plugin.expressionLanguage.ExpressionLanguageParser"
3+
4+
extends="com.intellij.extapi.psi.ASTWrapperPsiElement"
5+
6+
psiClassPrefix="ExpressionLanguage"
7+
psiImplClassSuffix="Impl"
8+
psiPackage="fr.adrienbrault.idea.symfony2plugin.expressionLanguage.psi"
9+
psiImplPackage="fr.adrienbrault.idea.symfony2plugin.expressionLanguage.psi.impl"
10+
11+
elementTypeHolderClass="fr.adrienbrault.idea.symfony2plugin.expressionLanguage.psi.ExpressionLanguageTypes"
12+
elementTypeClass="fr.adrienbrault.idea.symfony2plugin.expressionLanguage.psi.ExpressionLanguageElementType"
13+
tokenTypeClass="fr.adrienbrault.idea.symfony2plugin.expressionLanguage.psi.ExpressionLanguageTokenType"
14+
15+
extends(".*expr")=expr
16+
tokens=[
17+
space='regexp:\s+'
18+
number='regexp:\d+(\.\d*)?'
19+
null="regexp:NULL|null"
20+
true="regexp:TRUE|true"
21+
false="regexp:FALSE|false"
22+
id='regexp:\p{Alpha}\w*'
23+
string="regexp:('([^'\\]|\\.)*'|\"([^\"\\]|\\.)*\")"
24+
OP_OR="regexp:or|\|\|"
25+
OP_AND="regexp:and|&&"
26+
OP_BIT_OR="|"
27+
OP_BIT_XOR="^"
28+
OP_BIT_AND="&"
29+
OP_IDENTICAL="==="
30+
OP_EQ="=="
31+
OP_NOT_IDENTICAL="!=="
32+
OP_NEQ="!="
33+
OP_LT="<"
34+
OP_GT=">"
35+
OP_GTE=">="
36+
OP_LTE="<="
37+
OP_NOT_IN="not in"
38+
OP_IN="in"
39+
OP_MATCHES="matches"
40+
OP_RANGE=".."
41+
OP_PLUS="+"
42+
OP_MINUS="-"
43+
OP_CONCAT="~"
44+
OP_MUL="*"
45+
OP_DIV="/"
46+
OP_MOD="%"
47+
OP_POW="**"
48+
OP_NOT='regexp:not|\!'
49+
50+
L_ROUND_BRACKET="("
51+
R_ROUND_BRACKET=")"
52+
L_CURLY_BRACKET="{"
53+
R_CURLY_BRACKET="}"
54+
L_SQUARE_BRACKET="["
55+
R_SQUARE_BRACKET="]"
56+
57+
syntax='regexp:[?:.,]'
58+
]
59+
}
60+
61+
root ::= expr
62+
63+
expr ::= ternary_group
64+
| or_expr
65+
| and_expr
66+
| bit_or_expr
67+
| bit_xor_expr
68+
| bit_and_expr
69+
| rel_group
70+
| range_expr
71+
| add_group
72+
| concat_expr
73+
| mul_group
74+
| not_expr
75+
| exp_expr
76+
| sign_group
77+
| qualification_expr
78+
| array_access_expr
79+
| primary_group
80+
81+
private sign_group ::= unary_plus_expr | unary_min_expr
82+
private mul_group ::= mul_expr | div_expr | mod_expr
83+
private add_group ::= plus_expr | minus_expr
84+
private ternary_group ::= ternary_expr | elvis_expr
85+
86+
private rel_group ::= eq_strict_expr
87+
| eq_expr
88+
| neq_strict_expr
89+
| neq_expr
90+
| gte_expr
91+
| gt_expr
92+
| lt_expr
93+
| lte_expr
94+
| not_in_expr
95+
| in_expr
96+
| matches_expr
97+
98+
private primary_group ::= simple_ref_expr | literal_expr | array_expr | hash_expr | call_expr | paren_expr
99+
100+
not_expr ::= OP_NOT expr
101+
unary_min_expr ::= OP_MINUS expr
102+
unary_plus_expr ::= OP_PLUS expr
103+
div_expr ::= expr OP_DIV expr
104+
mul_expr ::= expr OP_MUL expr
105+
mod_expr ::= expr OP_MOD expr
106+
concat_expr ::= expr OP_CONCAT expr
107+
minus_expr ::= expr OP_MINUS expr
108+
plus_expr ::= expr OP_PLUS expr
109+
range_expr ::= expr OP_RANGE expr
110+
eq_strict_expr ::= expr OP_IDENTICAL expr
111+
neq_strict_expr ::= expr OP_NOT_IDENTICAL expr
112+
eq_expr ::= expr OP_EQ expr
113+
neq_expr ::= expr OP_NEQ expr
114+
gt_expr ::= expr OP_GT expr
115+
gte_expr ::= expr OP_GTE expr
116+
lt_expr ::= expr OP_LT expr
117+
lte_expr ::= expr OP_LTE expr
118+
not_in_expr ::= expr OP_NOT_IN expr
119+
in_expr ::= expr OP_IN expr
120+
matches_expr ::= expr OP_MATCHES expr
121+
or_expr ::= expr OP_OR expr
122+
and_expr ::= expr OP_AND expr
123+
bit_and_expr ::= expr OP_BIT_AND expr
124+
bit_or_expr ::= expr OP_BIT_OR expr
125+
bit_xor_expr ::= expr OP_BIT_XOR expr
126+
exp_expr ::= expr (OP_POW expr)+
127+
paren_expr ::= L_ROUND_BRACKET expr R_ROUND_BRACKET
128+
ternary_expr ::= expr '?' expr (':' expr)?
129+
elvis_expr ::= expr '?:' expr
130+
131+
fake ref_expr ::= expr? '.' identifier
132+
simple_ref_expr ::= identifier {extends=ref_expr elementType=ref_expr}
133+
qualification_expr ::= expr '.' identifier {extends=ref_expr elementType=ref_expr}
134+
array_access_expr ::= expr L_SQUARE_BRACKET expr R_SQUARE_BRACKET {extends=ref_expr elementType=ref_expr}
135+
136+
literal_expr ::= number | string | true | false | null
137+
array_expr ::= L_SQUARE_BRACKET expr_list? R_SQUARE_BRACKET
138+
hash_expr ::= L_CURLY_BRACKET hash_entries? R_CURLY_BRACKET
139+
140+
call_expr ::= expr L_ROUND_BRACKET expr_list? R_ROUND_BRACKET
141+
142+
private hash_entries ::= hash_entry (',' hash_entry)*
143+
private hash_entry ::= identifier ':' expr
144+
private expr_list ::= expr (',' expr_list)*
145+
146+
identifier ::= id
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package fr.adrienbrault.idea.symfony2plugin.expressionLanguage;
2+
3+
import com.intellij.lexer.FlexLexer;
4+
import com.intellij.psi.tree.IElementType;
5+
6+
import static com.intellij.psi.TokenType.BAD_CHARACTER;
7+
import static com.intellij.psi.TokenType.WHITE_SPACE;
8+
import static fr.adrienbrault.idea.symfony2plugin.expressionLanguage.psi.ExpressionLanguageTypes.*;
9+
10+
%%
11+
12+
%{
13+
public ExpressionLanguageLexer() {
14+
this((java.io.Reader)null);
15+
}
16+
%}
17+
18+
%public
19+
%class ExpressionLanguageLexer
20+
%implements FlexLexer
21+
%function advance
22+
%type IElementType
23+
%unicode
24+
25+
EOL=\R
26+
WHITE_SPACE=\s+
27+
28+
SPACE=[ \t\n\x0B\f\r]+
29+
NUMBER=[0-9]+(\.[0-9]*)?
30+
NULL=NULL|null
31+
TRUE=TRUE|true
32+
FALSE=FALSE|false
33+
ID=[:letter:][a-zA-Z_0-9]*
34+
STRING=('([^'\\]|\\.)*'|\"([^\"\\]|\\.)*\")
35+
OP_OR=or|\|\|
36+
OP_AND=and|&&
37+
OP_NOT=not|\!
38+
SYNTAX=[?:.,]
39+
40+
%%
41+
<YYINITIAL> {
42+
{WHITE_SPACE} { return WHITE_SPACE; }
43+
44+
"|" { return OP_BIT_OR; }
45+
"^" { return OP_BIT_XOR; }
46+
"&" { return OP_BIT_AND; }
47+
"===" { return OP_IDENTICAL; }
48+
"==" { return OP_EQ; }
49+
"!==" { return OP_NOT_IDENTICAL; }
50+
"!=" { return OP_NEQ; }
51+
"<" { return OP_LT; }
52+
">" { return OP_GT; }
53+
">=" { return OP_GTE; }
54+
"<=" { return OP_LTE; }
55+
"not in" { return OP_NOT_IN; }
56+
"in" { return OP_IN; }
57+
"matches" { return OP_MATCHES; }
58+
".." { return OP_RANGE; }
59+
"+" { return OP_PLUS; }
60+
"-" { return OP_MINUS; }
61+
"~" { return OP_CONCAT; }
62+
"*" { return OP_MUL; }
63+
"/" { return OP_DIV; }
64+
"%" { return OP_MOD; }
65+
"**" { return OP_POW; }
66+
"(" { return L_ROUND_BRACKET; }
67+
")" { return R_ROUND_BRACKET; }
68+
"{" { return L_CURLY_BRACKET; }
69+
"}" { return R_CURLY_BRACKET; }
70+
"[" { return L_SQUARE_BRACKET; }
71+
"]" { return R_SQUARE_BRACKET; }
72+
73+
{SPACE} { return SPACE; }
74+
{NUMBER} { return NUMBER; }
75+
{NULL} { return NULL; }
76+
{TRUE} { return TRUE; }
77+
{FALSE} { return FALSE; }
78+
{ID} { return ID; }
79+
{STRING} { return STRING; }
80+
{OP_OR} { return OP_OR; }
81+
{OP_AND} { return OP_AND; }
82+
{OP_NOT} { return OP_NOT; }
83+
{SYNTAX} { return SYNTAX; }
84+
85+
}
86+
87+
[^] { return BAD_CHARACTER; }
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package fr.adrienbrault.idea.symfony2plugin.expressionLanguage;
2+
3+
import com.intellij.lang.Language;
4+
5+
public class ExpressionLanguage extends Language {
6+
7+
public static final ExpressionLanguage INSTANCE = new ExpressionLanguage();
8+
9+
private ExpressionLanguage() {
10+
super("Symfony Expression Language");
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package fr.adrienbrault.idea.symfony2plugin.expressionLanguage;
2+
3+
import com.intellij.lang.BracePair;
4+
import com.intellij.lang.PairedBraceMatcher;
5+
import com.intellij.psi.PsiFile;
6+
import com.intellij.psi.tree.IElementType;
7+
import fr.adrienbrault.idea.symfony2plugin.expressionLanguage.psi.ExpressionLanguageTypes;
8+
import org.jetbrains.annotations.NotNull;
9+
import org.jetbrains.annotations.Nullable;
10+
11+
public class ExpressionLanguageBraceMatcher implements PairedBraceMatcher {
12+
private static BracePair[] PAIRS = {
13+
new BracePair(
14+
ExpressionLanguageTypes.L_ROUND_BRACKET,
15+
ExpressionLanguageTypes.R_ROUND_BRACKET,
16+
true
17+
),
18+
new BracePair(
19+
ExpressionLanguageTypes.L_SQUARE_BRACKET,
20+
ExpressionLanguageTypes.R_SQUARE_BRACKET,
21+
true
22+
),
23+
new BracePair(
24+
ExpressionLanguageTypes.L_CURLY_BRACKET,
25+
ExpressionLanguageTypes.R_CURLY_BRACKET,
26+
true
27+
),
28+
};
29+
30+
@Override
31+
public BracePair[] getPairs() {
32+
return PAIRS;
33+
}
34+
35+
@Override
36+
public boolean isPairedBracesAllowedBeforeType(@NotNull IElementType lbraceType, @Nullable IElementType contextType) {
37+
return false;
38+
}
39+
40+
@Override
41+
public int getCodeConstructStart(PsiFile file, int openingBraceOffset) {
42+
return openingBraceOffset;
43+
}
44+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package fr.adrienbrault.idea.symfony2plugin.expressionLanguage;
2+
3+
import com.intellij.openapi.fileTypes.LanguageFileType;
4+
import fr.adrienbrault.idea.symfony2plugin.Symfony2Icons;
5+
import org.jetbrains.annotations.NotNull;
6+
import org.jetbrains.annotations.Nullable;
7+
8+
import javax.swing.*;
9+
10+
public class ExpressionLanguageFileType extends LanguageFileType {
11+
12+
public static final ExpressionLanguageFileType INSTANCE = new ExpressionLanguageFileType();
13+
14+
private ExpressionLanguageFileType() {
15+
super(ExpressionLanguage.INSTANCE);
16+
}
17+
18+
@NotNull
19+
@Override
20+
public String getName() {
21+
return "Expression Language File";
22+
}
23+
24+
@NotNull
25+
@Override
26+
public String getDescription() {
27+
return "Expression Language file";
28+
}
29+
30+
@NotNull
31+
@Override
32+
public String getDefaultExtension() {
33+
return "sfel";
34+
}
35+
36+
@Override
37+
@Nullable
38+
public Icon getIcon() {
39+
return Symfony2Icons.SYMFONY;
40+
}
41+
}

0 commit comments

Comments
 (0)