Skip to content

Commit 5a62d32

Browse files
committed
[Fluid] Clean parser code and add first parser tests
1 parent ae97b5e commit 5a62d32

File tree

10 files changed

+100
-58
lines changed

10 files changed

+100
-58
lines changed

lang-fluid/src/main/grammars/FluidLexer.flex

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -26,32 +26,20 @@ EOL="\r"|"\n"|"\r\n"
2626
LINE_WS=[\ \t\f]
2727
WS=({LINE_WS}|{EOL})+
2828

29-
ALL_VARIABLE = '_all'
30-
3129
INTEGER_NUMBER = 0|[1-9]\d*
3230
FLOAT_NUMBER = [0-9]*\.[0-9]+([eE][-+]?[0-9]+)?|[0-9]+[eE][-+]?[0-9]+
3331
IDENTIFIER = [\p{Alpha}_][\p{Alnum}_]*
34-
VIEWHELPER_CALL = [\p{Alpha}_][\p{Alnum}_:]*
35-
VIEWHELPER_NAME= [a-zA-Z]*?(.[a-zA-Z])*
3632

3733
SINGLE_QUOTED_STRING=((\" ([^\"\n])* \"?) | ("'" ([^\'\n])* \'?))
3834
DOUBLE_QUOTED_STRING=((\" ([^\"\n])* \"?) | ("\"" ([^\"\n])* \"?))
3935

4036
NAMESPACE_DECL = [a-zA-Z\\\]+][\\\a-zA-Z]+]*
41-
NAMESPACE_ALIAS= [a-z]+
4237

4338
TEXT = [^{]*
4439

4540
%state EXPRESSION_LIST
4641
%state NAMESPACE_IMPORT
47-
%state VIEW_HELPER
48-
%state SECTION_NODE
49-
%state RENDER_NODE
50-
%state LAYOUT_NODE
51-
%state ARGUMENT_LIST
52-
%state SINGLE_QUOTED_STRING
53-
%state DOUBLE_QUOTED_STRING
54-
%state TERNARY_BRANCHES_OP
42+
%state ARRAY
5543
%state COMMENT
5644

5745
%%
@@ -65,27 +53,17 @@ TEXT = [^{]*
6553
[^] { yybegin(YYINITIAL); return TokenType.BAD_CHARACTER; }
6654
}
6755

68-
<LAYOUT_NODE> {
69-
{IDENTIFIER} { return FluidTypes.IDENTIFIER; }
70-
"=" { return FluidTypes.ASSIGN; }
71-
{DOUBLE_QUOTED_STRING} { return FluidTypes.DOUBLE_QUOTED_STRING; }
72-
{SINGLE_QUOTED_STRING} { return FluidTypes.SINGLE_QUOTED_STRING; }
73-
{WS}+ { return TokenType.WHITE_SPACE; }
74-
75-
"/>" { yybegin(YYINITIAL); return FluidTypes.TAG_END; }
76-
77-
[^] { yybegin(YYINITIAL); return TokenType.BAD_CHARACTER; }
78-
}
79-
8056
<EXPRESSION_LIST> {
8157
"}" { yybegin(YYINITIAL); return FluidTypes.EXPR_END; }
8258
"true" { return FluidTypes.TRUE; }
8359
"false" { return FluidTypes.FALSE; }
8460
"TRUE" { return FluidTypes.TRUE; }
8561
"FALSE" { return FluidTypes.FALSE; }
8662

87-
'_all' { return FluidTypes.VAR_ALL; }
63+
"{" { yybegin(ARRAY); return FluidTypes.EXPR_START; }
64+
8865
'as' { return FluidTypes.AS; }
66+
"namespace" { yybegin(NAMESPACE_IMPORT); return FluidTypes.NAMESPACE; }
8967

9068
"->" { return FluidTypes.ARROW; }
9169
":" { return FluidTypes.COLON; }
@@ -94,7 +72,6 @@ TEXT = [^{]*
9472

9573
{DOUBLE_QUOTED_STRING} { return FluidTypes.DOUBLE_QUOTED_STRING; }
9674
{SINGLE_QUOTED_STRING} { return FluidTypes.SINGLE_QUOTED_STRING; }
97-
"namespace" { yybegin(NAMESPACE_IMPORT); return FluidTypes.NAMESPACE; }
9875
{INTEGER_NUMBER} { return FluidTypes.NUMBER; }
9976
{FLOAT_NUMBER} { return FluidTypes.FLOAT_NUMBER; }
10077

@@ -131,6 +108,20 @@ TEXT = [^{]*
131108
[^] { yybegin(YYINITIAL); return TokenType.BAD_CHARACTER; }
132109
}
133110

111+
<ARRAY> {
112+
"}" { yybegin(YYINITIAL); return FluidTypes.EXPR_END; }
113+
":" { return FluidTypes.COLON; }
114+
{IDENTIFIER} { return FluidTypes.IDENTIFIER; }
115+
{DOUBLE_QUOTED_STRING} { return FluidTypes.DOUBLE_QUOTED_STRING; }
116+
{SINGLE_QUOTED_STRING} { return FluidTypes.SINGLE_QUOTED_STRING; }
117+
{INTEGER_NUMBER} { return FluidTypes.NUMBER; }
118+
{FLOAT_NUMBER} { return FluidTypes.FLOAT_NUMBER; }
119+
120+
{WS}+ { return TokenType.WHITE_SPACE; }
121+
122+
[^] { yybegin(YYINITIAL); return TokenType.BAD_CHARACTER; }
123+
}
124+
134125
<NAMESPACE_IMPORT> {
135126
"=" { return FluidTypes.ASSIGN; }
136127
{WS}+ { return TokenType.WHITE_SPACE; }

lang-fluid/src/main/grammars/FluidParser.bnf

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -64,20 +64,11 @@
6464

6565
COLON=':'
6666

67-
LAYOUT_TAG_START='<f:layout'
68-
TAG_END='/>'
69-
7067
NS='regexp:[a-zA-Z]+'
71-
VIEWHELPER_NAME='regexp:[a-zA-Z]*?(.[a-zA-Z])*'
7268
NAMESPACE_FQN='regexp:[a-zA-Z\\\]*]'
73-
NAMESPACE_ALIAS='regexp:[a-z]+'
7469

7570
NAMESPACE = 'namespace'
7671
AS = 'as'
77-
78-
CONTENT = 'content'
79-
80-
VAR_ALL = '_all'
8172
]
8273

8374
extends("string_literal|number_literal|boolean_literal|null_literal")=literal
@@ -94,7 +85,6 @@ File ::= StatementList
9485
private StatementList ::= ( TEXT+ | NamespaceStatement | InlineStatement )*
9586

9687
fake Statement ::=
97-
fake ViewHelperCall ::=
9888

9989
template_comment ::= "<!--/*" COMMENT_CONTENT? "*/-->"
10090

@@ -142,7 +132,7 @@ literal ::= string_literal | number_literal | boolean_literal | null_literal {
142132
mixin="com.cedricziel.idea.fluid.lang.psi.impl.FluidLiteralMixin"
143133
}
144134

145-
FieldChain ::= IDENT FieldChain?
135+
FieldChain ::= IDENTIFIER ('.' FieldChain)?
146136
FieldExpr ::= FieldChain
147137
FieldChainExpr ::= Expression FieldChain
148138

@@ -176,7 +166,7 @@ CastExpr ::= Expression as_op IDENTIFIER
176166
ViewHelperExpr ::= boundNamespace ':' viewHelperReference '(' viewHelperArgumentList ')' {
177167
mixin="com.cedricziel.idea.fluid.lang.psi.impl.FluidViewHelperNodeMixin"
178168
implements="com.cedricziel.idea.fluid.lang.psi.FluidPresentableName"
179-
pin=2
169+
pin=4
180170
}
181171
viewHelperReference ::= IDENTIFIER ('.' viewHelperPath)*
182172
private viewHelperPath ::= IDENTIFIER ( '.' viewHelperPath)*
@@ -195,22 +185,30 @@ viewHelperArgument ::= argumentKey ':' argumentValue {
195185
}
196186
argumentKey ::= IDENTIFIER
197187

198-
argumentValue ::= literal | IdentifierExpr
188+
argumentValue ::= literal | FieldExpr | ArrayCreationExpr
199189

200190
StringExpr ::= string_literal
201-
IdentifierExpr ::= IDENTIFIER ('.' PropertyAccessExpr)?
202-
PropertyAccessExpr ::= IDENTIFIER ( '.' PropertyAccessExpr)?
203191

204192
NumberExpr ::= NUMBER
205193

206194
ParenthesesExpr ::= '(' InlineChain ')' { pin=1 }
207195

208-
ArrayCreationExpr ::= '{' ArrayElement ( ',' ArrayElement )* '}' { pin=1 }
196+
ArrayCreationExpr ::= InlineArray | NestedArray {
197+
pin=1
198+
}
199+
private InlineArray ::= ArrayElement ( ',' ArrayElement )* {
200+
pin=1
201+
}
202+
private NestedArray ::= '{' ArrayElement ( ',' ArrayElement )* '}' {
203+
pin=2
204+
}
205+
209206
ArrayElement ::= ArrayKey ':' ArrayValue {
210207
pin=2
211208
}
212-
ArrayKey ::= IDENTIFIER
213-
ArrayValue ::= literal | IdentifierExpr
209+
210+
ArrayKey ::= IDENTIFIER | literal
211+
ArrayValue ::= literal | FieldExpr | NestedArray
214212

215213
InlineChain ::= Expression Chain? {
216214
pin=1
@@ -222,7 +220,7 @@ Chain ::= '->' Expression Chain? { pin=1 }
222220

223221
private ViewHelperGroup ::= ViewHelperExpr
224222
private FieldGroup ::= FieldExpr | FieldChainExpr
225-
private PrimaryGroup ::= NumberExpr | IdentifierExpr | StringExpr | ParenthesesExpr | ArrayCreationExpr
223+
private PrimaryGroup ::= NumberExpr | FieldExpr | StringExpr | ParenthesesExpr | ArrayCreationExpr
226224

227225
InlineStatement ::= '{' InlineChain '}' {
228226
pin=2

lang-fluid/src/main/java/com/cedricziel/idea/fluid/FluidViewHelperReferenceInsertHandler.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import com.intellij.codeInsight.lookup.LookupElement;
1313
import com.intellij.codeInsight.template.Template;
1414
import com.intellij.codeInsight.template.TemplateManager;
15-
import com.intellij.codeInsight.template.impl.EmptyNode;
1615
import com.intellij.codeInsight.template.impl.TextExpression;
1716
import com.intellij.openapi.editor.Editor;
1817
import com.intellij.openapi.project.Project;
@@ -39,8 +38,8 @@ public void handleInsert(InsertionContext context, LookupElement item) {
3938
viewHelper = findViewHelperByReference((FluidViewHelperReference) completionParent, item);
4039
} else if(completionParent instanceof FluidBoundNamespace) {
4140
// find viewhelper
42-
} else if (completionParent instanceof FluidIdentifierExpr) {
43-
viewHelper = findViewHelperByIdentifierPosition((FluidIdentifierExpr) completionParent, item);
41+
} else if (completionParent instanceof FluidFieldChain) {
42+
viewHelper = findViewHelperByFieldPosition((FluidFieldChain) completionParent, item);
4443
}
4544

4645
if (viewHelper == null) {
@@ -65,7 +64,7 @@ public void handleInsert(InsertionContext context, LookupElement item) {
6564
TemplateManager.getInstance(context.getProject()).startTemplate(context.getEditor(), t);
6665
}
6766

68-
private ViewHelper findViewHelperByIdentifierPosition(@NotNull FluidIdentifierExpr completionParent, @NotNull LookupElement item) {
67+
private ViewHelper findViewHelperByFieldPosition(@NotNull FluidFieldChain completionParent, @NotNull LookupElement item) {
6968
String[] split = StringUtils.split(item.getLookupString(), ":");
7069
if (split.length != 2) {
7170
return null;

lang-fluid/src/main/java/com/cedricziel/idea/fluid/codeInsight/inspection/UndefinedVariableInspection.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.cedricziel.idea.fluid.codeInsight.inspection;
22

3-
import com.cedricziel.idea.fluid.lang.psi.FluidIdentifierExpr;
3+
import com.cedricziel.idea.fluid.lang.psi.FluidFieldChain;
44
import com.cedricziel.idea.fluid.util.FluidUtil;
55
import com.intellij.codeInspection.LocalInspectionTool;
66
import com.intellij.codeInspection.ProblemHighlightType;
@@ -16,7 +16,7 @@ public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean is
1616
return new PsiElementVisitor() {
1717
@Override
1818
public void visitElement(PsiElement element) {
19-
if (!(element instanceof FluidIdentifierExpr)) {
19+
if (!(element instanceof FluidFieldChain)) {
2020
super.visitElement(element);
2121
return;
2222
}

lang-fluid/src/main/java/com/cedricziel/idea/fluid/codeInsight/template/postfix/templates/ForEachPostfixTemplate.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.cedricziel.idea.fluid.codeInsight.template.postfix.templates;
22

3-
import com.cedricziel.idea.fluid.lang.psi.FluidIdentifierExpr;
3+
import com.cedricziel.idea.fluid.lang.psi.FluidFieldChain;
44
import com.cedricziel.idea.fluid.lang.psi.FluidInlineStatement;
55
import com.cedricziel.idea.fluid.lang.psi.FluidTypes;
66
import com.intellij.codeInsight.template.postfix.templates.PostfixTemplate;
@@ -22,7 +22,7 @@ protected ForEachPostfixTemplate() {
2222
@Override
2323
public boolean isApplicable(@NotNull PsiElement context, @NotNull Document copyDocument, int newOffset) {
2424
return PlatformPatterns.psiElement(FluidTypes.IDENTIFIER)
25-
.withParent(PlatformPatterns.psiElement(FluidIdentifierExpr.class))
25+
.withParent(PlatformPatterns.psiElement(FluidFieldChain.class))
2626
.accepts(context);
2727
}
2828

lang-fluid/src/main/java/com/cedricziel/idea/fluid/completion/FluidCompletionContributor.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ protected void addCompletions(@NotNull CompletionParameters parameters, Processi
2121
}
2222
});
2323

24-
extend(CompletionType.BASIC, PlatformPatterns.psiElement(FluidTypes.IDENTIFIER).withParent(FluidIdentifierExpr.class), new CompletionProvider<CompletionParameters>() {
24+
extend(CompletionType.BASIC, PlatformPatterns.psiElement(FluidTypes.IDENTIFIER).withParent(FluidFieldChain.class), new CompletionProvider<CompletionParameters>() {
2525
@Override
2626
protected void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context, @NotNull CompletionResultSet result) {
2727
for (VariableProvider extension : VariableProvider.EP_NAME.getExtensions()) {
@@ -30,7 +30,7 @@ protected void addCompletions(@NotNull CompletionParameters parameters, Processi
3030
}
3131
});
3232

33-
extend(CompletionType.BASIC, PlatformPatterns.psiElement(FluidTypes.IDENTIFIER).withParent(FluidIdentifierExpr.class).afterLeaf("->"), new CompletionProvider<CompletionParameters>() {
33+
extend(CompletionType.BASIC, PlatformPatterns.psiElement(FluidTypes.IDENTIFIER).withParent(FluidFieldChain.class).afterLeaf("->"), new CompletionProvider<CompletionParameters>() {
3434
@Override
3535
protected void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context, @NotNull CompletionResultSet result) {
3636
for (NamespaceProvider extension : NamespaceProvider.EP_NAME.getExtensions()) {

lang-fluid/src/main/java/com/cedricziel/idea/fluid/lang/FluidFileHighlighter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ public Lexer getHighlightingLexer() {
9999

100100
@NotNull
101101
public TextAttributesKey[] getTokenHighlights(IElementType tokenType) {
102-
if (tokenType.equals(FluidTypes.IDENTIFIER_EXPR) || tokenType.equals(FluidTypes.IDENTIFIER) || tokenType.equals(FluidTypes.NAMESPACE)) {
102+
if (tokenType.equals(FluidTypes.FIELD_CHAIN) || tokenType.equals(FluidTypes.IDENTIFIER) || tokenType.equals(FluidTypes.NAMESPACE)) {
103103
return IDENTIFIER_KEYS;
104104
} else if (tokenType.equals(FluidTypes.EXPR_END) || tokenType.equals(FluidTypes.EXPR_START)) {
105105
return BRACES_KEYS;

lang-fluid/src/main/java/com/cedricziel/idea/fluid/viewHelpers/DefaultViewHelpersProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public Map<String, ViewHelper> provideForNamespace(@NotNull Project project, @No
2929
return new THashMap<>();
3030
}
3131

32-
String schemaLocation = "/schemas/fluid/6.2.xsd";
32+
String schemaLocation = "/schemas/fluid/7.6.xsd";
3333
String schema = readSchema(schemaLocation);
3434

3535
XmlFile xmlLanguage = (XmlFile) PsiFileFactory.getInstance(project).createFileFromText(XMLLanguage.INSTANCE, schema);

lang-fluid/src/test/java/com/cedricziel/idea/fluid/lang/.gitkeep

Whitespace-only changes.
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package com.cedricziel.idea.fluid.lang.parser;
2+
3+
import com.cedricziel.idea.fluid.lang.psi.*;
4+
import com.intellij.psi.PsiElement;
5+
import com.intellij.psi.PsiFile;
6+
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
7+
8+
public class ParserTest extends LightCodeInsightFixtureTestCase {
9+
public void testCanDetectVariables() {
10+
assertParentElementAtCaretMatchesType("{fo<caret>o}", FluidFieldChain.class);
11+
assertParentElementAtCaretMatchesType("{fo<caret>o -> f:fo()}", FluidFieldChain.class);
12+
13+
assertParentElementAtCaretMatchesType("{foo -> f:fo(fo<caret>o: 'bar')}", FluidArgumentKey.class);
14+
assertParentElementAtCaretMatchesType("{foo -> f:fo(foo: 'ba<caret>r')}", FluidStringLiteral.class);
15+
16+
assertParentElementAtCaretMatchesType("{foo -> f:fo(foo: 'ba<caret>r')}", FluidStringLiteral.class);
17+
18+
assertParentElementAtCaretMatchesType("{foo.ba<caret>r}", FluidFieldChain.class);
19+
}
20+
21+
public void testCanDetectViewHelpers() {
22+
assertParentElementAtCaretMatchesType("{f:f<caret>o()}", FluidViewHelperReference.class);
23+
assertParentElementAtCaretMatchesType("{foo -> f:f<caret>o()}", FluidViewHelperReference.class);
24+
}
25+
26+
public void testCanDetectNamespaceDeclarations() {
27+
assertParentElementAtCaretMatchesType("{name<caret>space foo=Bar')}", FluidNamespaceStatement.class);
28+
assertParentElementAtCaretMatchesType("{name<caret>space foo=Bar\\Baz')}", FluidNamespaceStatement.class);
29+
assertParentElementAtCaretMatchesType("{name<caret>space foo=Bar\\Baz\\Buz')}", FluidNamespaceStatement.class);
30+
}
31+
32+
public void testCanDetectExpressions() {
33+
assertParentElementAtCaretMatchesType("{foo <caret>+ bar)}", FluidAdditiveExpr.class);
34+
assertParentElementAtCaretMatchesType("{foo <caret>- bar)}", FluidAdditiveExpr.class);
35+
36+
assertParentElementAtCaretMatchesType("{foo <caret>* bar)}", FluidMultiplicativeExpr.class);
37+
assertParentElementAtCaretMatchesType("{foo <caret>/ bar)}", FluidMultiplicativeExpr.class);
38+
assertParentElementAtCaretMatchesType("{foo <caret>% bar)}", FluidMultiplicativeExpr.class);
39+
}
40+
41+
public void testCanDetectBooleanLiterals() {
42+
assertParentElementAtCaretMatchesType("{foo -> f:fo(foo: <caret>true)}", FluidBooleanLiteral.class);
43+
assertParentElementAtCaretMatchesType("{foo -> f:fo(foo: <caret>false)}", FluidBooleanLiteral.class);
44+
}
45+
46+
private void assertParentElementAtCaretMatchesType(String content, Class expected) {
47+
PsiFile psiFile = myFixture.configureByText("foo.fluid", content);
48+
int offset = myFixture.getEditor().getCaretModel().getOffset();
49+
50+
PsiElement elementAt = psiFile.findElementAt(offset);
51+
52+
assertInstanceOf(elementAt.getParent(), expected);
53+
}
54+
}

0 commit comments

Comments
 (0)