Skip to content

Commit bd5d401

Browse files
committed
Generator: introduce "visitor-value" option to generate Visitor<Type>
1 parent 9c4d98a commit bd5d401

23 files changed

+792
-325
lines changed

grammars/Grammar.bnf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
classHeader="license.txt"
3-
generate=[names="long"]
3+
generate=[names="long" visitor-value="R"]
44

55
parserClass="org.intellij.grammar.parser.GrammarParser"
66
parserUtilClass="org.intellij.grammar.parser.GrammarParserUtil"

support/messages/attributeDescriptions/generate.html

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,21 @@
1414
<td><b>yes</b>, no</td>
1515
<td>PSI: generate PSI classes</td>
1616
</tr>
17+
<tr>
18+
<td>psi-factory</td>
19+
<td><b>yes</b>, no</td>
20+
<td>PSI: generate PsiElement for ASTNode node factory</td>
21+
</tr>
22+
<tr>
23+
<td>visitor</td>
24+
<td><b>yes</b>, no</td>
25+
<td>PSI: generate visitor for PSI classes</td>
26+
</tr>
27+
<tr>
28+
<td>visitor-value</td>
29+
<td><b>void</b>, name of type parameter</td>
30+
<td>PSI: generate visitor with generic argument</td>
31+
</tr>
1732
<tr>
1833
<td>elements</td>
1934
<td><b>yes</b>, no</td>

support/org/intellij/grammar/analysis/BnfFirstNextAnalyzer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
public class BnfFirstNextAnalyzer {
4444

4545
private static final Logger LOG = Logger.getInstance("org.intellij.grammar.analysis.BnfFirstNextAnalyzer");
46-
46+
4747
public static final String MATCHES_EOF = "-eof-";
4848
public static final String MATCHES_NOTHING = "-never-matches-";
4949
public static final String MATCHES_ANY = "-any-";

support/org/intellij/grammar/generator/FakeBnfExpression.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.intellij.psi.tree.IElementType;
2121
import org.intellij.grammar.psi.BnfExpression;
2222
import org.intellij.grammar.psi.BnfTypes;
23+
import org.intellij.grammar.psi.BnfVisitor;
2324
import org.jetbrains.annotations.NotNull;
2425

2526
/**
@@ -34,6 +35,11 @@ public FakeBnfExpression(@NotNull IElementType elementType, @NotNull String text
3435
super(elementType, text);
3536
}
3637

38+
@Override
39+
public <R> R accept(@NotNull BnfVisitor<R> visitor) {
40+
return null;
41+
}
42+
3743
@Override
3844
public String toString() {
3945
return getText();

support/org/intellij/grammar/generator/GenOptions.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.intellij.grammar.generator;
1818

19+
import com.intellij.openapi.util.text.StringUtil;
1920
import com.intellij.util.PatternUtil;
2021
import org.intellij.grammar.KnownAttribute;
2122
import org.intellij.grammar.psi.BnfFile;
@@ -39,6 +40,7 @@ public class GenOptions {
3940
public final boolean generatePsi;
4041
public final boolean generatePsiFactory;
4142
public final boolean generateVisitor;
43+
public final String visitorValue;
4244
public final Case generateTokenCase;
4345
public final Case generateElementCase;
4446
public final boolean generateTokenAccessors;
@@ -55,6 +57,7 @@ public GenOptions(BnfFile myFile) {
5557
generateTokenAccessors = getGenerateOption(myFile, KnownAttribute.GENERATE_TOKEN_ACCESSORS, genOptions.get("tokenAccessors"));
5658
generateRootRules = PatternUtil.compileSafe(genOptions.get("root-rules"), null);
5759
generateVisitor = !"no".equals(genOptions.get("visitor"));
60+
visitorValue = "void".equals(genOptions.get("visitor-value")) ? null : StringUtil.nullize(genOptions.get("visitor-value"));
5861

5962
generateTokenCase = ParserGeneratorUtil.enumFromString(genOptions.get("token-case"), Case.UPPER);
6063
generateElementCase = ParserGeneratorUtil.enumFromString(genOptions.get("element-case"), Case.UPPER);

support/org/intellij/grammar/generator/ParserGenerator.java

Lines changed: 51 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import com.intellij.util.Function;
2929
import com.intellij.util.ObjectUtils;
3030
import com.intellij.util.containers.ContainerUtil;
31+
import com.intellij.util.containers.JBIterable;
3132
import com.intellij.util.containers.MultiMap;
3233
import gnu.trove.THashMap;
3334
import gnu.trove.THashSet;
@@ -246,13 +247,16 @@ private void generateVisitor(String psiClass, Map<String, BnfRule> sortedRules)
246247
for (BnfRule rule : sortedRules.values()) {
247248
imports.addAll(getSuperInterfaceNames(myFile, rule, StringUtil.getPackageName(psiClass), myRuleClassPrefix));
248249
}
249-
generateClassHeader(psiClass, imports, "", false, BnfConstants.PSI_ELEMENT_VISITOR_CLASS);
250+
String r = G.visitorValue != null ? "<" + G.visitorValue + ">" : "";
251+
String t = G.visitorValue != null ? G.visitorValue : "void";
252+
String ret = G.visitorValue != null ? "return " : "";
253+
generateClassHeader(psiClass + r, imports, "", false, BnfConstants.PSI_ELEMENT_VISITOR_CLASS);
250254
Set<String> visited = new HashSet<String>();
251255
Set<String> all = new TreeSet<String>();
252256
for (BnfRule rule : sortedRules.values()) {
253257
String methodName = getRulePsiClassName(rule, "");
254258
visited.add(methodName);
255-
out("public void visit" + methodName + "(@NotNull " + getRulePsiClassName(rule, myRuleClassPrefix) + " o) {");
259+
out("public " + t + " visit" + methodName + "(@NotNull " + getRulePsiClassName(rule, myRuleClassPrefix) + " o) {");
256260
boolean first = true;
257261
for (String top : getSuperInterfaceNames(myFile, rule, "", myRuleClassPrefix)) {
258262
if (!first && top.equals(superIntf)) continue;
@@ -264,7 +268,7 @@ private void generateVisitor(String psiClass, Map<String, BnfRule> sortedRules)
264268
}
265269
String text = "visit" + top + "(o);";
266270
if (first) {
267-
out(text);
271+
out(ret + text);
268272
}
269273
else {
270274
out("// " + text);
@@ -275,15 +279,16 @@ private void generateVisitor(String psiClass, Map<String, BnfRule> sortedRules)
275279
newLine();
276280
}
277281
all.remove(shortSuperIntf);
278-
for (String top : ContainerUtil.concat(all, Arrays.asList(shortSuperIntf))) {
282+
for (String top : JBIterable.from(all).append(shortSuperIntf)) {
279283
String methodName = top.startsWith(myRuleClassPrefix) ? top.substring(myRuleClassPrefix.length()) : top;
280284
if (visited.contains(methodName)) continue;
281-
out("public void visit" + methodName + "(@NotNull " + myShortener.fun(top) + " o) {");
285+
out("public " + t + " visit" + methodName + "(@NotNull " + myShortener.fun(top) + " o) {");
282286
if (!methodName.equals(top) && !top.equals(shortSuperIntf)) {
283-
out("visit" + (shortSuperIntf.startsWith(myRuleClassPrefix) ? shortSuperIntf.substring(myRuleClassPrefix.length()) : shortSuperIntf) + "(o);");
287+
out(ret + "visit" + (shortSuperIntf.startsWith(myRuleClassPrefix) ? shortSuperIntf.substring(myRuleClassPrefix.length()) : shortSuperIntf) + "(o);");
284288
}
285289
else {
286290
out("visitElement(o);");
291+
if (G.visitorValue != null) out(ret + "null;");
287292
}
288293
out("}");
289294
newLine();
@@ -471,34 +476,8 @@ private void generateClassHeader(String className,
471476
String shortClassName = StringUtil.getShortName(className);
472477
out("package %s;", packageName);
473478
newLine();
474-
final Set<String> realImports = new HashSet<String>();
475-
realImports.add(packageName + ".*");
476-
Function<String, String> shortener = new Function<String, String>() {
477-
@Override
478-
public String fun(String s) {
479-
boolean changed = false;
480-
StringBuilder sb = new StringBuilder();
481-
boolean vararg = s.endsWith("...");
482-
for (String part : StringUtil.tokenize(new StringTokenizer(StringUtil.trimEnd(s, "..."), TYPE_TEXT_SEPARATORS, true))) {
483-
String pkg;
484-
String wildcard = part.startsWith("? super ") ? "? super " : part.startsWith("? extends ") ? "? extends " : "";
485-
part = StringUtil.trimStart(part, wildcard);
486-
if (TYPE_TEXT_SEPARATORS.contains(part)) {
487-
sb.append(part).append(part.equals(",") ? " " : "");
488-
}
489-
else if (realImports.contains(part) ||
490-
"java.lang".equals(pkg = StringUtil.getPackageName(part)) ||
491-
realImports.contains(pkg + ".*")) {
492-
sb.append(wildcard).append(StringUtil.getShortName(part));
493-
changed = true;
494-
}
495-
else {
496-
sb.append(wildcard).append(part);
497-
}
498-
}
499-
return changed ? sb.append(vararg? "..." : "").toString() : s;
500-
}
501-
};
479+
Set<String> realImports = ContainerUtil.newLinkedHashSet(packageName + ".*");
480+
Function<String, String> shortener = newClassNameShortener(realImports);
502481
for (String item : imports) {
503482
for (String s : StringUtil.tokenize(item.replaceAll("\\s+", " "), TYPE_TEXT_SEPARATORS)) {
504483
s = StringUtil.trimStart(StringUtil.trimStart(s, "? super "), "? extends ");
@@ -534,6 +513,36 @@ else if (!intf && i == 1) {
534513
myShortener = shortener;
535514
}
536515

516+
@NotNull
517+
private static Function<String, String> newClassNameShortener(final Set<String> realImports) {
518+
return new Function<String, String>() {
519+
@Override
520+
public String fun(String s) {
521+
boolean changed = false;
522+
StringBuilder sb = new StringBuilder();
523+
boolean vararg = s.endsWith("...");
524+
for (String part : StringUtil.tokenize(new StringTokenizer(StringUtil.trimEnd(s, "..."), TYPE_TEXT_SEPARATORS, true))) {
525+
String pkg;
526+
String wildcard = part.startsWith("? super ") ? "? super " : part.startsWith("? extends ") ? "? extends " : "";
527+
part = StringUtil.trimStart(part, wildcard);
528+
if (TYPE_TEXT_SEPARATORS.contains(part)) {
529+
sb.append(part).append(part.equals(",") ? " " : "");
530+
}
531+
else if (realImports.contains(part) ||
532+
"java.lang".equals(pkg = StringUtil.getPackageName(part)) ||
533+
realImports.contains(pkg + ".*")) {
534+
sb.append(wildcard).append(StringUtil.getShortName(part));
535+
changed = true;
536+
}
537+
else {
538+
sb.append(wildcard).append(part);
539+
}
540+
}
541+
return changed ? sb.append(vararg? "..." : "").toString() : s;
542+
}
543+
};
544+
}
545+
537546
private void generateFileHeader(String className) {
538547
final String classHeader = getStringOrFile(getRootAttribute(myFile, KnownAttribute.CLASS_HEADER, className));
539548
out(classHeader);
@@ -1307,8 +1316,15 @@ private void generatePsiImpl(BnfRule rule,
13071316
newLine();
13081317
}
13091318
if (visitorClassName != null) {
1319+
String r = G.visitorValue != null ? "<" + G.visitorValue + ">" : "";
1320+
String t = G.visitorValue != null ? " " + G.visitorValue : "void";
1321+
String ret = G.visitorValue != null ? "return " : "";
1322+
out("public " + r + t + " accept(@NotNull " + visitorClassName + r + " visitor) {");
1323+
out(ret + "visitor.visit" + getRulePsiClassName(rule, "") + "(this);");
1324+
out("}");
1325+
newLine();
13101326
out("public void accept(@NotNull " + myShortener.fun(BnfConstants.PSI_ELEMENT_VISITOR_CLASS) + " visitor) {");
1311-
out("if (visitor instanceof " + visitorClassName + ") ((" + visitorClassName + ")visitor).visit" + getRulePsiClassName(rule, "") + "(this);");
1327+
out("if (visitor instanceof " + visitorClassName + ") accept((" + visitorClassName + ")visitor);");
13121328
out("else super.accept(visitor);");
13131329
out("}");
13141330
newLine();

support/org/intellij/grammar/generator/ParserGeneratorUtil.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -543,10 +543,10 @@ public static Map<String, String> collectTokenPattern2Name(@NotNull final BnfFil
543543
final int[] autoCount = {0};
544544
final Set<String> origTokenNames = ContainerUtil.newLinkedHashSet(origTokens.values());
545545

546-
GrammarUtil.visitRecursively(file, true, new BnfVisitor() {
546+
BnfVisitor<Void> visitor = new BnfVisitor<Void>() {
547547

548548
@Override
549-
public void visitStringLiteralExpression(@NotNull BnfStringLiteralExpression o) {
549+
public Void visitStringLiteralExpression(@NotNull BnfStringLiteralExpression o) {
550550
String text = o.getText();
551551
String tokenText = StringUtil.stripQuotesAroundValue(text);
552552
// add auto-XXX token for all unmatched strings to avoid BAD_CHARACTER's
@@ -561,19 +561,24 @@ public void visitStringLiteralExpression(@NotNull BnfStringLiteralExpression o)
561561
else {
562562
ContainerUtil.addIfNotNull(usedNames, origTokens.get(tokenText));
563563
}
564+
return null;
564565
}
565566

566567
@Override
567-
public void visitReferenceOrToken(@NotNull BnfReferenceOrToken o) {
568-
if (GrammarUtil.isExternalReference(o)) return;
568+
public Void visitReferenceOrToken(@NotNull BnfReferenceOrToken o) {
569+
if (GrammarUtil.isExternalReference(o)) return null;
569570
BnfRule rule = o.resolveRule();
570-
if (rule != null) return;
571+
if (rule != null) return null;
571572
String tokenName = o.getText();
572573
if (usedNames.add(tokenName) && !origTokenNames.contains(tokenName)) {
573574
map.put(tokenName, tokenName);
574575
}
576+
return null;
575577
}
576-
});
578+
};
579+
for (BnfExpression o : GrammarUtil.bnfTraverserNoAttrs(file).filter(BnfExpression.class)) {
580+
o.accept(visitor);
581+
}
577582
// fix ordering: origTokens go _after_ to handle keywords correctly
578583
for (String tokenText : origTokens.keySet()) {
579584
String tokenName = origTokens.get(tokenText);

support/org/intellij/grammar/inspection/BnfDuplicateRuleInspection.java

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,8 @@
2020
import com.intellij.codeInspection.LocalInspectionTool;
2121
import com.intellij.codeInspection.ProblemDescriptor;
2222
import com.intellij.codeInspection.ProblemsHolder;
23-
import com.intellij.psi.PsiElement;
2423
import com.intellij.psi.PsiFile;
25-
import com.intellij.util.Processor;
26-
import gnu.trove.THashSet;
24+
import com.intellij.util.containers.ContainerUtil;
2725
import org.intellij.grammar.psi.BnfFile;
2826
import org.intellij.grammar.psi.BnfRule;
2927
import org.intellij.grammar.psi.impl.GrammarUtil;
@@ -71,23 +69,18 @@ public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull InspectionM
7169
return problemsHolder.getResultsArray();
7270
}
7371

74-
private static void checkFile(final PsiFile file, final ProblemsHolder problemsHolder) {
72+
private static void checkFile(PsiFile file, ProblemsHolder problemsHolder) {
7573
if (!(file instanceof BnfFile)) return;
76-
final BnfFile bnfFile = (BnfFile)file;
74+
BnfFile bnfFile = (BnfFile)file;
7775

78-
final Set<BnfRule> rules = new THashSet<BnfRule>();
79-
GrammarUtil.processChildrenDummyAware(file, new Processor<PsiElement>() {
80-
@Override
81-
public boolean process(PsiElement psiElement) {
82-
String name = psiElement instanceof BnfRule ? ((BnfRule)psiElement).getName() : null;
83-
BnfRule rule = name == null? null : bnfFile.getRule(name);
84-
if (name != null && rule != psiElement) {
85-
rules.add(rule);
86-
rules.add((BnfRule)psiElement);
87-
}
88-
return true;
76+
Set<BnfRule> rules = ContainerUtil.newLinkedHashSet();
77+
for (BnfRule r : GrammarUtil.bnfTraverser(bnfFile).filter(BnfRule.class)) {
78+
BnfRule t = bnfFile.getRule(r.getName());
79+
if (r != t) {
80+
rules.add(t);
81+
rules.add(r);
8982
}
90-
});
83+
}
9184
for (BnfRule rule : rules) {
9285
problemsHolder.registerProblem(rule.getId(), "'" + rule.getName() + "' rule is defined more than once");
9386
}

support/org/intellij/grammar/psi/BnfCompositeElement.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,15 @@
1616
package org.intellij.grammar.psi;
1717

1818
import com.intellij.psi.PsiElement;
19+
import org.jetbrains.annotations.NotNull;
1920

2021
/**
2122
* User: gregory
2223
* Date: 13.07.11
2324
* Time: 19:02
2425
*/
2526
public interface BnfCompositeElement extends PsiElement {
27+
28+
<R> R accept(@NotNull BnfVisitor<R> visitor);
29+
2630
}

support/org/intellij/grammar/psi/impl/BnfCompositeElementImpl.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,8 @@
1818
import com.intellij.extapi.psi.ASTWrapperPsiElement;
1919
import com.intellij.lang.ASTNode;
2020
import com.intellij.openapi.util.text.StringUtil;
21-
import org.intellij.grammar.psi.BnfCompositeElement;
22-
import org.intellij.grammar.psi.BnfExpression;
23-
import org.intellij.grammar.psi.BnfLiteralExpression;
24-
import org.intellij.grammar.psi.BnfValueList;
21+
import org.intellij.grammar.psi.*;
22+
import org.jetbrains.annotations.NotNull;
2523

2624
/**
2725
* Created by IntelliJ IDEA.
@@ -50,4 +48,9 @@ public String toString() {
5048
return elementType;
5149
}
5250
}
51+
52+
@Override
53+
public <R> R accept(@NotNull BnfVisitor<R> visitor) {
54+
return visitor.visitCompositeElement(this);
55+
}
5356
}

0 commit comments

Comments
 (0)