Skip to content

Commit 9984ff1

Browse files
committed
Impl. Expression Language Parser
1 parent a8623ee commit 9984ff1

11 files changed

+434
-0
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.12.3"
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: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
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*)?([Ee][+\-]\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="||"
25+
OP_OR_KW="or"
26+
OP_AND="&&"
27+
OP_AND_KW="and"
28+
OP_BIT_OR="|"
29+
OP_BIT_XOR="^"
30+
OP_BIT_AND="&"
31+
OP_IDENTICAL="==="
32+
OP_EQ="=="
33+
OP_NOT_IDENTICAL="!=="
34+
OP_NEQ="!="
35+
OP_LT="<"
36+
OP_GT=">"
37+
OP_GTE=">="
38+
OP_LTE="<="
39+
OP_NOT_IN="not in"
40+
OP_IN="in"
41+
OP_MATCHES="matches"
42+
OP_RANGE=".."
43+
OP_PLUS="+"
44+
OP_MINUS="-"
45+
OP_CONCAT="~"
46+
OP_MUL="*"
47+
OP_DIV="/"
48+
OP_MOD="%"
49+
OP_POW="**"
50+
OP_NOT='!'
51+
OP_NOT_KW='not'
52+
53+
L_ROUND_BRACKET="("
54+
R_ROUND_BRACKET=")"
55+
L_CURLY_BRACKET="{"
56+
R_CURLY_BRACKET="}"
57+
L_SQUARE_BRACKET="["
58+
R_SQUARE_BRACKET="]"
59+
60+
syntax='regexp:[?:.,]'
61+
]
62+
}
63+
64+
root ::= expr
65+
66+
expr ::= ternary_group
67+
| or_expr
68+
| and_expr
69+
| bit_or_expr
70+
| bit_xor_expr
71+
| bit_and_expr
72+
| rel_group
73+
| range_expr
74+
| add_group
75+
| concat_expr
76+
| mul_group
77+
| not_expr
78+
| exp_expr
79+
| sign_group
80+
| qualification_expr
81+
| array_access_expr
82+
| primary_group
83+
84+
private sign_group ::= unary_plus_expr | unary_min_expr
85+
private mul_group ::= mul_expr | div_expr | mod_expr
86+
private add_group ::= plus_expr | minus_expr
87+
private ternary_group ::= elvis_expr | ternary_expr
88+
89+
private rel_group ::= identical_expr
90+
| eq_expr
91+
| not_identical_expr
92+
| neq_expr
93+
| gte_expr
94+
| gt_expr
95+
| lt_expr
96+
| lte_expr
97+
| not_in_expr
98+
| in_expr
99+
| matches_expr
100+
101+
private primary_group ::= simple_ref_expr | literal_expr | array_expr | hash_expr | call_expr | paren_expr
102+
103+
not_expr ::= (OP_NOT|OP_NOT_KW) expr
104+
unary_min_expr ::= OP_MINUS expr
105+
unary_plus_expr ::= OP_PLUS expr
106+
div_expr ::= expr OP_DIV expr
107+
mul_expr ::= expr OP_MUL expr
108+
mod_expr ::= expr OP_MOD expr
109+
concat_expr ::= expr OP_CONCAT expr
110+
minus_expr ::= expr OP_MINUS expr
111+
plus_expr ::= expr OP_PLUS expr
112+
range_expr ::= expr OP_RANGE expr
113+
identical_expr ::= expr OP_IDENTICAL expr
114+
not_identical_expr ::= expr OP_NOT_IDENTICAL expr
115+
eq_expr ::= expr OP_EQ expr
116+
neq_expr ::= expr OP_NEQ expr
117+
gt_expr ::= expr OP_GT expr
118+
gte_expr ::= expr OP_GTE expr
119+
lt_expr ::= expr OP_LT expr
120+
lte_expr ::= expr OP_LTE expr
121+
not_in_expr ::= expr OP_NOT_IN expr
122+
in_expr ::= expr OP_IN expr
123+
matches_expr ::= expr OP_MATCHES expr
124+
or_expr ::= expr (OP_OR|OP_OR_KW) expr
125+
and_expr ::= expr (OP_AND|OP_AND_KW) expr
126+
bit_and_expr ::= expr OP_BIT_AND expr
127+
bit_or_expr ::= expr OP_BIT_OR expr
128+
bit_xor_expr ::= expr OP_BIT_XOR expr
129+
exp_expr ::= expr (OP_POW expr)+
130+
paren_expr ::= L_ROUND_BRACKET expr R_ROUND_BRACKET
131+
ternary_expr ::= expr '?' expr (':' expr)?
132+
elvis_expr ::= expr '?:' expr
133+
134+
fake ref_expr ::= expr? '.' identifier
135+
simple_ref_expr ::= identifier {extends=ref_expr elementType=ref_expr}
136+
qualification_expr ::= expr '.' identifier {extends=ref_expr elementType=ref_expr}
137+
array_access_expr ::= expr L_SQUARE_BRACKET expr R_SQUARE_BRACKET {extends=ref_expr elementType=ref_expr}
138+
139+
literal_expr ::= number | string | true | false | null
140+
array_expr ::= L_SQUARE_BRACKET expr_list? R_SQUARE_BRACKET
141+
hash_expr ::= L_CURLY_BRACKET hash_entries? R_CURLY_BRACKET
142+
143+
call_expr ::= expr L_ROUND_BRACKET expr_list? R_ROUND_BRACKET
144+
145+
private hash_entries ::= hash_entry (',' hash_entry)*
146+
private hash_entry ::= identifier ':' expr
147+
private expr_list ::= expr (',' expr_list)*
148+
149+
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]*)?([Ee][+\-][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+
SYNTAX=[?:.,]
36+
37+
%%
38+
<YYINITIAL> {
39+
{WHITE_SPACE} { return WHITE_SPACE; }
40+
41+
"||" { return OP_OR; }
42+
"or" { return OP_OR_KW; }
43+
"&&" { return OP_AND; }
44+
"and" { return OP_AND_KW; }
45+
"|" { return OP_BIT_OR; }
46+
"^" { return OP_BIT_XOR; }
47+
"&" { return OP_BIT_AND; }
48+
"===" { return OP_IDENTICAL; }
49+
"==" { return OP_EQ; }
50+
"!==" { return OP_NOT_IDENTICAL; }
51+
"!=" { return OP_NEQ; }
52+
"<" { return OP_LT; }
53+
">" { return OP_GT; }
54+
">=" { return OP_GTE; }
55+
"<=" { return OP_LTE; }
56+
"not in" { return OP_NOT_IN; }
57+
"in" { return OP_IN; }
58+
"matches" { return OP_MATCHES; }
59+
".." { return OP_RANGE; }
60+
"+" { return OP_PLUS; }
61+
"-" { return OP_MINUS; }
62+
"~" { return OP_CONCAT; }
63+
"*" { return OP_MUL; }
64+
"/" { return OP_DIV; }
65+
"%" { return OP_MOD; }
66+
"**" { return OP_POW; }
67+
"!" { return OP_NOT; }
68+
"not" { return OP_NOT_KW; }
69+
"(" { return L_ROUND_BRACKET; }
70+
")" { return R_ROUND_BRACKET; }
71+
"{" { return L_CURLY_BRACKET; }
72+
"}" { return R_CURLY_BRACKET; }
73+
"[" { return L_SQUARE_BRACKET; }
74+
"]" { return R_SQUARE_BRACKET; }
75+
76+
{SPACE} { return SPACE; }
77+
{NUMBER} { return NUMBER; }
78+
{NULL} { return NULL; }
79+
{TRUE} { return TRUE; }
80+
{FALSE} { return FALSE; }
81+
{ID} { return ID; }
82+
{STRING} { return STRING; }
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+
}
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+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package fr.adrienbrault.idea.symfony2plugin.expressionLanguage;
2+
3+
import com.intellij.lexer.FlexAdapter;
4+
5+
public class ExpressionLanguageLexerAdapter extends FlexAdapter {
6+
public ExpressionLanguageLexerAdapter() {
7+
super(new ExpressionLanguageLexer());
8+
}
9+
}

0 commit comments

Comments
 (0)