Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add attribute mixedAST to simplify mixed AST+PSI creation #316

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions resources/messages/attributeDescriptions/mixedAST.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<html>
<body>
Generate mixed PSI+AST implementation. Such implementations usually extend
`com.intellij.psi.impl.source.tree.CompositePsiElement` class.

<h2>Examples:</h2>
<pre><code>
{
mixedAST = true
}
my_named ::=
</code></pre>

</body>
</html>
1 change: 1 addition & 0 deletions src/org/intellij/grammar/KnownAttribute.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public class KnownAttribute<T> {
public static final KnownAttribute<String> MIXIN = create(false, String.class, "mixin", null);
public static final KnownAttribute<String> RECOVER_WHILE = create(false, String.class, "recoverWhile", null);
public static final KnownAttribute<String> NAME = create(false, String.class, "name", null);
public static final KnownAttribute<Boolean> MIXED_AST = create(false, Boolean.class, "mixedAST", false);

public static final KnownAttribute<Boolean> EXTRA_ROOT = create(false, Boolean.class, "extraRoot", false);
public static final KnownAttribute<Boolean> RIGHT_ASSOCIATIVE = create(false, Boolean.class, "rightAssociative", false);
Expand Down
37 changes: 24 additions & 13 deletions src/org/intellij/grammar/generator/ParserGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ static class RuleInfo {
RuleInfo(String name, boolean isFake,
String elementType, String parserClass,
String intfPackage, String implPackage,
String intfClass, String implClass, String mixin, String stub) {
String intfClass, String implClass, String mixin, String stub,
Boolean mixedAST) {
this.name = name;
this.isFake = isFake;
this.elementType = elementType;
Expand All @@ -93,6 +94,7 @@ static class RuleInfo {
this.intfClass = intfPackage + "." + intfClass;
this.implClass = implPackage + "." + implClass;
this.mixin = mixin;
this.mixedAST = mixedAST && stub == null;
}
}

Expand Down Expand Up @@ -194,7 +196,8 @@ public ParserGenerator(@NotNull BnfFile psiFile,
noPsi ? null : getAttribute(r, KnownAttribute.PSI_PACKAGE),
noPsi ? null : getAttribute(r, KnownAttribute.PSI_IMPL_PACKAGE),
noPsi ? null : getRulePsiClassName(r, myIntfClassFormat), noPsi ? null : getRulePsiClassName(r, myImplClassFormat),
noPsi ? null : getAttribute(r, KnownAttribute.MIXIN), noPsi ? null : getAttribute(r, KnownAttribute.STUB_CLASS)));
noPsi ? null : getAttribute(r, KnownAttribute.MIXIN), noPsi ? null : getAttribute(r, KnownAttribute.STUB_CLASS),
!noPsi && getAttribute(r, KnownAttribute.MIXED_AST)));
}
myGrammarRootParser = rootRule == null ? null : ruleInfo(rootRule).parserClass;
myNoStubs = JBIterable.from(myRuleInfos.values()).find(o -> o.stub != null) == null;
Expand Down Expand Up @@ -279,10 +282,10 @@ private void calcRealSuperClasses(Map<String, BnfRule> sortedPsiRules) {
superRuleClass.contains("?") ? superRuleClass.replaceAll("\\?", stubName) : superRuleClass;
// mixin attribute overrides "extends":
info.realSuperClass = StringUtil.notNullize(info.mixin, adjustedSuperRuleClass);
info.mixedAST = topInfo != null ? topInfo.mixedAST : JBIterable.of(superRuleClass, info.realSuperClass)
info.mixedAST = info.mixedAST || (topInfo != null ? topInfo.mixedAST : JBIterable.of(superRuleClass, info.realSuperClass)
.map(NameShortener::getRawClassName)
.flatMap(s -> JBTreeTraverser.<String>from(o -> JBIterable.of(myJavaHelper.getSuperClassName(o))).withRoot(s).unique())
.find(COMPOSITE_PSI_ELEMENT_CLASS::equals) != null;
.find(COMPOSITE_PSI_ELEMENT_CLASS::equals) != null);
}
}

Expand Down Expand Up @@ -1760,6 +1763,7 @@ private void generatePsiImpl(BnfRule rule, RuleInfo info) {
for (NavigatablePsiElement m : constructors) {
collectMethodTypesToImport(Collections.singletonList(m), false, imports);
}
if (info.mixedAST && constructors.isEmpty()) imports.add(IELEMENTTYPE_CLASS);
if (stubName != null && constructors.isEmpty()) imports.add(ISTUBELEMENTTYPE_CLASS);
if (stubName != null) imports.add(stubName);
}
Expand All @@ -1780,15 +1784,22 @@ private void generatePsiImpl(BnfRule rule, RuleInfo info) {
generateClassHeader(psiClass, imports, "", javaType, implSuper, superInterface);
String shortName = StringUtil.getShortName(psiClass);
if (constructors.isEmpty()) {
out("public " + shortName + "(" + shorten(AST_NODE_CLASS) + " node) {");
out("super(node);");
out("}");
newLine();
if (stubName != null) {
out("public " + shortName + "(" +
shorten(stubName) + " stub, " +
shorten(ISTUBELEMENTTYPE_CLASS) + " stubType) {");
out("super(stub, stubType);");
if (!info.mixedAST) {
out("public " + shortName + "(" + shorten(AST_NODE_CLASS) + " node) {");
out("super(node);");
out("}");
newLine();
if (stubName != null) {
out("public " + shortName + "(" +
shorten(stubName) + " stub, " +
shorten(ISTUBELEMENTTYPE_CLASS) + " stubType) {");
out("super(stub, stubType);");
out("}");
newLine();
}
} else {
out("public " + shortName + "(" + shorten(IELEMENTTYPE_CLASS) + " type) {");
out("super(type);");
out("}");
newLine();
}
Expand Down
228 changes: 228 additions & 0 deletions testData/generator/MixedAST.PSI.expected.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
// ---- FooTypes.java -----------------
//header.txt
package test;

import com.intellij.psi.tree.IElementType;
import com.intellij.psi.PsiElement;
import com.intellij.lang.ASTNode;
import test.psi.impl.*;
import com.intellij.psi.impl.source.tree.CompositePsiElement;

public interface FooTypes {

IElementType ELEMENT_1 = FooParserDefinition.createType("ELEMENT_1");
IElementType ELEMENT_2 = FooParserDefinition.createType("ELEMENT_2");
IElementType ELEMENT_3 = FooParserDefinition.createType("ELEMENT_3");

IElementType AA = FooParserDefinition.createTokenType("aa");
IElementType BB = FooParserDefinition.createTokenType("bb");

class Factory {
public static PsiElement createElement(ASTNode node) {
IElementType type = node.getElementType();
if (type == ELEMENT_2) {
return new Element2Impl(node);
}
else if (type == ELEMENT_3) {
return new Element3Impl(node);
}
throw new AssertionError("Unknown element type: " + type);
}

public static CompositePsiElement createElement(IElementType type) {
if (type == ELEMENT_1) {
return new Element1Impl(type);
}
throw new AssertionError("Unknown element type: " + type);
}
}
}
// ---- Element1.java -----------------
//header.txt
package test.psi;

import java.util.List;
import org.jetbrains.annotations.*;
import com.intellij.psi.PsiElement;

public interface Element1 extends PsiElement {

@NotNull
PsiElement getAa();

}
// ---- Element2.java -----------------
//header.txt
package test.psi;

import java.util.List;
import org.jetbrains.annotations.*;
import com.intellij.psi.PsiElement;

public interface Element2 extends PsiElement {

@NotNull
PsiElement getBb();

}
// ---- Element3.java -----------------
//header.txt
package test.psi;

import java.util.List;
import org.jetbrains.annotations.*;
import com.intellij.psi.PsiElement;
import com.intellij.psi.StubBasedPsiElement;
import test.stub.Element3Stub;

public interface Element3 extends PsiElement, StubBasedPsiElement<Element3Stub> {

@NotNull
PsiElement getBb();

}
// ---- Element1Impl.java -----------------
//header.txt
package test.psi.impl;

import java.util.List;
import org.jetbrains.annotations.*;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import test.psi.MyPsiTreeUtil;
import static test.FooTypes.*;
import test.CompositePsiElementImpl;
import test.psi.*;
import com.intellij.psi.tree.IElementType;

public class Element1Impl extends CompositePsiElementImpl implements Element1 {

public Element1Impl(IElementType type) {
super(type);
}

public void accept(@NotNull Visitor visitor) {
visitor.visitElement1(this);
}

@Override
public void accept(@NotNull PsiElementVisitor visitor) {
if (visitor instanceof Visitor) accept((Visitor)visitor);
else super.accept(visitor);
}

@Override
@NotNull
public PsiElement getAa() {
return findPsiChildByType(AA);
}

}
// ---- Element2Impl.java -----------------
//header.txt
package test.psi.impl;

import java.util.List;
import org.jetbrains.annotations.*;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import test.psi.MyPsiTreeUtil;
import static test.FooTypes.*;
import test.AstDelegatedPsiElementImpl;
import test.psi.*;

public class Element2Impl extends AstDelegatedPsiElementImpl implements Element2 {

public Element2Impl(ASTNode node) {
super(node);
}

public void accept(@NotNull Visitor visitor) {
visitor.visitElement2(this);
}

@Override
public void accept(@NotNull PsiElementVisitor visitor) {
if (visitor instanceof Visitor) accept((Visitor)visitor);
else super.accept(visitor);
}

@Override
@NotNull
public PsiElement getBb() {
return notNullChild(findChildByType(BB));
}

}
// ---- Element3Impl.java -----------------
//header.txt
package test.psi.impl;

import java.util.List;
import org.jetbrains.annotations.*;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import test.psi.MyPsiTreeUtil;
import static test.FooTypes.*;
import test.StubBasedPsiElementImpl;
import test.stub.Element3Stub;
import test.psi.*;
import com.intellij.psi.stubs.IStubElementType;

public class Element3Impl extends StubBasedPsiElementImpl<Element3Stub> implements Element3 {

public Element3Impl(ASTNode node) {
super(node);
}

public Element3Impl(Element3Stub stub, IStubElementType stubType) {
super(stub, stubType);
}

public void accept(@NotNull Visitor visitor) {
visitor.visitElement3(this);
}

@Override
public void accept(@NotNull PsiElementVisitor visitor) {
if (visitor instanceof Visitor) accept((Visitor)visitor);
else super.accept(visitor);
}

@Override
@NotNull
public PsiElement getBb() {
return notNullChild(findChildByType(BB));
}

}
// ---- Visitor.java -----------------
//header.txt
package test.psi;

import org.jetbrains.annotations.*;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.PsiElement;

public class Visitor extends PsiElementVisitor {

public void visitElement1(@NotNull Element1 o) {
visitPsiElement(o);
}

public void visitElement2(@NotNull Element2 o) {
visitPsiElement(o);
}

public void visitElement3(@NotNull Element3 o) {
visitPsiElement(o);
}

public void visitPsiElement(@NotNull PsiElement o) {
visitElement(o);
}

}
32 changes: 32 additions & 0 deletions testData/generator/MixedAST.bnf
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
generatePsi=true
classHeader="//header.txt"
psiPackage="test.psi"
psiImplPackage="test.psi.impl"
psiTreeUtilClass="test.psi.MyPsiTreeUtil"
parserClass="test.FooParser"
elementTypeFactory="test.FooParserDefinition.createType"
tokenTypeFactory="test.FooParserDefinition.createTokenType"
elementTypeHolderClass="test.FooTypes"
parserUtilClass="org.intellij.grammar.parser.GeneratedParserUtilBase"
expressionUtilClass="test.FooUtil"
generateTokenAccessors=true
mixedAST = true

extends='test.CompositePsiElementImpl'

tokens = [
AA = 'aa'
BB = 'bb'
]
}
root ::= element1 | element2 | element3
element1 ::= AA
element2 ::= BB {
mixedAST = false
extends='test.AstDelegatedPsiElementImpl'
}
element3 ::= BB {
stubClass="test.stub.Element3Stub"
extends='test.StubBasedPsiElementImpl<?>'
}
Loading