Refactoring transpiler.
This commit is contained in:
parent
dd92d99afd
commit
d116b2c555
@ -135,7 +135,12 @@ public class DefaultAstBuilderVisitor extends WebViewComponentsParserBaseVisitor
|
||||
children.add((QuestionTagChild) childResult);
|
||||
}
|
||||
}
|
||||
return this.nodeFactory.questionTagNode(this.getTokenRange(ctx), children);
|
||||
return this.nodeFactory.questionTagNode(
|
||||
this.getTokenRange(ctx),
|
||||
ctx.QuestionTagOpen().getSymbol(),
|
||||
ctx.QuestionTagClose().getSymbol(),
|
||||
children
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -147,7 +152,12 @@ public class DefaultAstBuilderVisitor extends WebViewComponentsParserBaseVisitor
|
||||
children.add((HtmlCommentChild) childResult);
|
||||
}
|
||||
}
|
||||
return this.nodeFactory.htmlCommentNode(this.getTokenRange(ctx), children);
|
||||
return this.nodeFactory.htmlCommentNode(
|
||||
this.getTokenRange(ctx),
|
||||
ctx.HtmlCommentOpen().getSymbol(),
|
||||
ctx.HtmlCommentClose().getSymbol(),
|
||||
children
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -340,7 +350,7 @@ public class DefaultAstBuilderVisitor extends WebViewComponentsParserBaseVisitor
|
||||
if (groovyCode != null) {
|
||||
return this.nodeFactory.equalsScriptletNode(
|
||||
this.getTokenRange(ctx),
|
||||
ctx.GroovyCode().getSymbol().getTokenIndex()
|
||||
ctx.GroovyCode().getSymbol().getText()
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
@ -363,7 +373,7 @@ public class DefaultAstBuilderVisitor extends WebViewComponentsParserBaseVisitor
|
||||
if (groovyCode != null) {
|
||||
return this.nodeFactory.dollarScriptletNode(
|
||||
this.getTokenRange(ctx),
|
||||
groovyCode.getSymbol().getTokenIndex()
|
||||
groovyCode.getSymbol().getText()
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
|
@ -7,6 +7,7 @@ import groowt.view.component.web.antlr.TokenList;
|
||||
import groowt.view.component.web.ast.extension.*;
|
||||
import groowt.view.component.web.ast.node.*;
|
||||
import groowt.view.component.web.util.TokenRange;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
@ -109,13 +110,23 @@ public class DefaultNodeFactory implements NodeFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public QuestionNode questionTagNode(TokenRange tokenRange, List<? extends QuestionTagChild> children) {
|
||||
return this.objectFactory.get(QuestionNode.class, tokenRange, children);
|
||||
public QuestionNode questionTagNode(
|
||||
TokenRange tokenRange,
|
||||
Token openToken,
|
||||
Token closeToken,
|
||||
List<? extends QuestionTagChild> children
|
||||
) {
|
||||
return this.objectFactory.get(QuestionNode.class, tokenRange, openToken, closeToken, children);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HtmlCommentNode htmlCommentNode(TokenRange tokenRange, List<? extends HtmlCommentChild> children) {
|
||||
return this.objectFactory.get(HtmlCommentNode.class, tokenRange, children);
|
||||
public HtmlCommentNode htmlCommentNode(
|
||||
TokenRange tokenRange,
|
||||
Token openToken,
|
||||
Token closeToken,
|
||||
List<? extends HtmlCommentChild> children
|
||||
) {
|
||||
return this.objectFactory.get(HtmlCommentNode.class, tokenRange, openToken, closeToken, children);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -209,8 +220,8 @@ public class DefaultNodeFactory implements NodeFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public EqualsScriptletNode equalsScriptletNode(TokenRange tokenRange, int groovyIndex) {
|
||||
return this.objectFactory.get(EqualsScriptletNode.class, tokenRange, groovyIndex);
|
||||
public EqualsScriptletNode equalsScriptletNode(TokenRange tokenRange, String groovyCode) {
|
||||
return this.objectFactory.get(EqualsScriptletNode.class, tokenRange, groovyCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -219,8 +230,8 @@ public class DefaultNodeFactory implements NodeFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DollarScriptletNode dollarScriptletNode(TokenRange tokenRange, int groovyIndex) {
|
||||
return this.objectFactory.get(DollarScriptletNode.class, tokenRange, groovyIndex);
|
||||
public DollarScriptletNode dollarScriptletNode(TokenRange tokenRange, String groovyCode) {
|
||||
return this.objectFactory.get(DollarScriptletNode.class, tokenRange, groovyCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -2,6 +2,7 @@ package groowt.view.component.web.ast;
|
||||
|
||||
import groowt.view.component.web.ast.node.*;
|
||||
import groowt.view.component.web.util.TokenRange;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
@ -20,9 +21,19 @@ public interface NodeFactory {
|
||||
|
||||
BodyTextNode bodyTextNode(TokenRange tokenRange, List<? extends BodyTextChild> children);
|
||||
|
||||
QuestionNode questionTagNode(TokenRange tokenRange, List<? extends QuestionTagChild> children);
|
||||
QuestionNode questionTagNode(
|
||||
TokenRange tokenRange,
|
||||
Token openToken,
|
||||
Token closeToken,
|
||||
List<? extends QuestionTagChild> children
|
||||
);
|
||||
|
||||
HtmlCommentNode htmlCommentNode(TokenRange tokenRange, List<? extends HtmlCommentChild> children);
|
||||
HtmlCommentNode htmlCommentNode(
|
||||
TokenRange tokenRange,
|
||||
Token openToken,
|
||||
Token closeToken,
|
||||
List<? extends HtmlCommentChild> children
|
||||
);
|
||||
|
||||
TextNode textNode(TokenRange tokenRange, String content);
|
||||
|
||||
@ -63,11 +74,11 @@ public interface NodeFactory {
|
||||
|
||||
ComponentValueNode componentValueNode(TokenRange tokenRange, ComponentNode componentNode);
|
||||
|
||||
EqualsScriptletNode equalsScriptletNode(TokenRange tokenRange, int groovyIndex);
|
||||
EqualsScriptletNode equalsScriptletNode(TokenRange tokenRange, String groovyCode);
|
||||
|
||||
PlainScriptletNode plainScriptletNode(TokenRange tokenRange, int groovyIndex);
|
||||
|
||||
DollarScriptletNode dollarScriptletNode(TokenRange tokenRange, int groovyIndex);
|
||||
DollarScriptletNode dollarScriptletNode(TokenRange tokenRange, String groovyCode);
|
||||
|
||||
DollarReferenceNode dollarReferenceNode(TokenRange tokenRange, int groovyIndex);
|
||||
|
||||
|
@ -6,6 +6,7 @@ import org.antlr.v4.runtime.Token;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Deprecated
|
||||
public abstract sealed class GStringNodeExtension implements NodeExtension
|
||||
permits GStringPathExtension, GStringScriptletExtension {
|
||||
|
||||
|
@ -6,6 +6,7 @@ import groowt.view.component.web.ast.node.Node;
|
||||
import groowt.view.component.web.util.TokenRange;
|
||||
import jakarta.inject.Inject;
|
||||
|
||||
@Deprecated
|
||||
public non-sealed class GStringPathExtension extends GStringNodeExtension {
|
||||
|
||||
@Inject
|
||||
|
@ -6,6 +6,7 @@ import groowt.view.component.web.ast.node.Node;
|
||||
import groowt.view.component.web.util.TokenRange;
|
||||
import jakarta.inject.Inject;
|
||||
|
||||
@Deprecated
|
||||
public non-sealed class GStringScriptletExtension extends GStringNodeExtension {
|
||||
|
||||
@Inject
|
||||
|
@ -29,4 +29,8 @@ public class BodyTextNode extends AbstractTreeNode implements BodyChildNode {
|
||||
super(tokenRange, extensionContainer, childrenAsNodes(checkChildren(children)));
|
||||
}
|
||||
|
||||
public List<BodyTextChild> getChildrenAsBodyTextChildren() {
|
||||
return this.getChildren().stream().map(BodyTextChild.class::cast).toList();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,34 +1,28 @@
|
||||
package groowt.view.component.web.ast.node;
|
||||
|
||||
import groowt.util.di.annotation.Given;
|
||||
import groowt.view.component.web.antlr.TokenList;
|
||||
import groowt.view.component.web.ast.extension.GStringPathExtension;
|
||||
import groowt.view.component.web.ast.extension.NodeExtensionContainer;
|
||||
import groowt.view.component.web.util.TokenRange;
|
||||
import jakarta.inject.Inject;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DollarReferenceNode extends AbstractLeafNode implements GroovyBodyNode {
|
||||
|
||||
private final int groovyTokenIndex;
|
||||
private final List<String> parts;
|
||||
|
||||
@Inject
|
||||
public DollarReferenceNode(
|
||||
TokenList tokenList,
|
||||
NodeExtensionContainer extensionContainer,
|
||||
@Given TokenRange tokenRange,
|
||||
@Given int groovyTokenIndex
|
||||
List<String> parts
|
||||
) {
|
||||
super(tokenRange, extensionContainer);
|
||||
this.groovyTokenIndex = groovyTokenIndex;
|
||||
this.createGStringPath(tokenList);
|
||||
this.parts = parts;
|
||||
}
|
||||
|
||||
protected void createGStringPath(TokenList tokenList) {
|
||||
this.createExtension(GStringPathExtension.class, TokenRange.fromIndex(tokenList, this.groovyTokenIndex));
|
||||
}
|
||||
|
||||
public GStringPathExtension getGStringPath() {
|
||||
return this.getExtension(GStringPathExtension.class);
|
||||
public List<String> getParts() {
|
||||
return this.parts;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,33 +1,26 @@
|
||||
package groowt.view.component.web.ast.node;
|
||||
|
||||
import groowt.util.di.annotation.Given;
|
||||
import groowt.view.component.web.antlr.TokenList;
|
||||
import groowt.view.component.web.ast.extension.GStringScriptletExtension;
|
||||
import groowt.view.component.web.ast.extension.NodeExtensionContainer;
|
||||
import groowt.view.component.web.util.TokenRange;
|
||||
import jakarta.inject.Inject;
|
||||
|
||||
public class DollarScriptletNode extends AbstractLeafNode implements GroovyBodyNode {
|
||||
|
||||
private final GStringScriptletExtension gStringScriptlet;
|
||||
private final String groovyCode;
|
||||
|
||||
@Inject
|
||||
public DollarScriptletNode(
|
||||
TokenList tokenList,
|
||||
NodeExtensionContainer extensionContainer,
|
||||
@Given TokenRange tokenRange,
|
||||
@Given int groovyTokenIndex
|
||||
@Given String groovyCode
|
||||
) {
|
||||
super(tokenRange, extensionContainer);
|
||||
this.gStringScriptlet = this.createGStringScriptlet(tokenList, groovyTokenIndex);
|
||||
this.groovyCode = groovyCode;
|
||||
}
|
||||
|
||||
protected GStringScriptletExtension createGStringScriptlet(TokenList tokenList, int groovyTokenIndex) {
|
||||
return this.createExtension(GStringScriptletExtension.class, TokenRange.fromIndex(tokenList, groovyTokenIndex));
|
||||
}
|
||||
|
||||
public GStringScriptletExtension getGStringScriptlet() {
|
||||
return this.gStringScriptlet;
|
||||
public String getGroovyCode() {
|
||||
return this.groovyCode;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,49 +1,23 @@
|
||||
package groowt.view.component.web.ast.node;
|
||||
|
||||
import groowt.util.di.annotation.Given;
|
||||
import groowt.view.component.web.antlr.TokenList;
|
||||
import groowt.view.component.web.ast.extension.GroovyCodeNodeExtension;
|
||||
import groowt.view.component.web.ast.extension.NodeExtensionContainer;
|
||||
import groowt.view.component.web.util.TokenRange;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class EqualsScriptletNode extends AbstractLeafNode implements GroovyBodyNode {
|
||||
|
||||
private final int groovyIndex;
|
||||
private final GroovyCodeNodeExtension groovyCode;
|
||||
private final String groovyCode;
|
||||
|
||||
public EqualsScriptletNode(
|
||||
TokenList tokenList,
|
||||
NodeExtensionContainer extensionContainer,
|
||||
@Given TokenRange tokenRange,
|
||||
@Given int groovyIndex
|
||||
@Given String groovyCode
|
||||
) {
|
||||
super(tokenRange, extensionContainer);
|
||||
this.groovyIndex = groovyIndex;
|
||||
this.groovyCode = this.createGroovyCode(tokenList);
|
||||
this.groovyCode = groovyCode;
|
||||
}
|
||||
|
||||
protected GroovyCodeNodeExtension createGroovyCode(TokenList tokenList) {
|
||||
return this.createExtension(
|
||||
GroovyCodeNodeExtension.class,
|
||||
TokenRange.fromIndex(tokenList, this.groovyIndex),
|
||||
(Function<? super List<Token>, String>) this::toValidGroovyCode
|
||||
);
|
||||
}
|
||||
|
||||
protected String toValidGroovyCode(List<Token> groovyTokens) {
|
||||
return "{ -> " + groovyTokens.stream().map(Token::getText).collect(Collectors.joining()) + " }";
|
||||
}
|
||||
|
||||
public int getGroovyIndex() {
|
||||
return this.groovyIndex;
|
||||
}
|
||||
|
||||
public GroovyCodeNodeExtension getGroovyCode() {
|
||||
public String getGroovyCode() {
|
||||
return this.groovyCode;
|
||||
}
|
||||
|
||||
|
@ -4,18 +4,38 @@ import groowt.util.di.annotation.Given;
|
||||
import groowt.view.component.web.ast.extension.NodeExtensionContainer;
|
||||
import groowt.view.component.web.util.TokenRange;
|
||||
import jakarta.inject.Inject;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class HtmlCommentNode extends AbstractTreeNode implements BodyTextChild {
|
||||
|
||||
private final Token openToken;
|
||||
private final Token closeToken;
|
||||
|
||||
@Inject
|
||||
public HtmlCommentNode(
|
||||
NodeExtensionContainer extensionContainer,
|
||||
@Given TokenRange tokenRange,
|
||||
@Given Token openToken,
|
||||
@Given Token closeToken,
|
||||
@Given List<? extends HtmlCommentChild> children
|
||||
) {
|
||||
super(tokenRange, extensionContainer, children.stream().map(HtmlCommentChild::asNode).toList());
|
||||
this.openToken = openToken;
|
||||
this.closeToken = closeToken;
|
||||
}
|
||||
|
||||
public List<HtmlCommentChild> getChildrenAsHtmlCommentChildren() {
|
||||
return this.getChildren().stream().map(HtmlCommentChild.class::cast).toList();
|
||||
}
|
||||
|
||||
public Token getOpenToken() {
|
||||
return this.openToken;
|
||||
}
|
||||
|
||||
public Token getCloseToken() {
|
||||
return this.closeToken;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,50 +2,26 @@ package groowt.view.component.web.ast.node;
|
||||
|
||||
import groowt.util.di.annotation.Given;
|
||||
import groowt.view.component.web.antlr.TokenList;
|
||||
import groowt.view.component.web.ast.extension.GroovyCodeNodeExtension;
|
||||
import groowt.view.component.web.ast.extension.NodeExtensionContainer;
|
||||
import groowt.view.component.web.util.TokenRange;
|
||||
import jakarta.inject.Inject;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class PlainScriptletNode extends AbstractLeafNode implements GroovyBodyNode {
|
||||
|
||||
private final int groovyIndex;
|
||||
private final GroovyCodeNodeExtension groovyCode;
|
||||
private final String groovyCode;
|
||||
|
||||
@Inject
|
||||
public PlainScriptletNode(
|
||||
TokenList tokenList,
|
||||
NodeExtensionContainer extensionContainer,
|
||||
@Given TokenRange tokenRange,
|
||||
@Given int groovyIndex
|
||||
@Given String groovyCode
|
||||
) {
|
||||
super(tokenRange, extensionContainer);
|
||||
this.groovyIndex = groovyIndex;
|
||||
this.groovyCode = this.createGroovyCode(tokenList);
|
||||
this.groovyCode = groovyCode;
|
||||
}
|
||||
|
||||
protected GroovyCodeNodeExtension createGroovyCode(TokenList tokenList) {
|
||||
return this.createExtension(
|
||||
GroovyCodeNodeExtension.class,
|
||||
TokenRange.fromIndex(tokenList, this.groovyIndex),
|
||||
(Function<? super List<Token>, String>) this::toValidGroovyCode
|
||||
);
|
||||
}
|
||||
|
||||
protected String toValidGroovyCode(List<Token> groovyTokens) {
|
||||
return "{ Writer out -> " + groovyTokens.stream().map(Token::getText).collect(Collectors.joining()) + " }";
|
||||
}
|
||||
|
||||
public int getGroovyIndex() {
|
||||
return this.groovyIndex;
|
||||
}
|
||||
|
||||
public GroovyCodeNodeExtension getGroovyCode() {
|
||||
public String getGroovyCode() {
|
||||
return this.groovyCode;
|
||||
}
|
||||
|
||||
|
@ -4,18 +4,38 @@ import groowt.util.di.annotation.Given;
|
||||
import groowt.view.component.web.ast.extension.NodeExtensionContainer;
|
||||
import groowt.view.component.web.util.TokenRange;
|
||||
import jakarta.inject.Inject;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class QuestionNode extends AbstractTreeNode implements BodyTextChild {
|
||||
|
||||
private final Token openToken;
|
||||
private final Token closeToken;
|
||||
|
||||
@Inject
|
||||
public QuestionNode(
|
||||
NodeExtensionContainer extensionContainer,
|
||||
@Given TokenRange tokenRange,
|
||||
@Given Token openToken,
|
||||
@Given Token closeToken,
|
||||
@Given List<? extends QuestionTagChild> children
|
||||
) {
|
||||
super(tokenRange, extensionContainer, children.stream().map(QuestionTagChild::asNode).toList());
|
||||
this.openToken = openToken;
|
||||
this.closeToken = closeToken;
|
||||
}
|
||||
|
||||
public List<QuestionTagChild> getChildrenAsQuestionTagChildren() {
|
||||
return this.getChildren().stream().map(QuestionTagChild.class::cast).toList();
|
||||
}
|
||||
|
||||
public Token getOpenToken() {
|
||||
return this.openToken;
|
||||
}
|
||||
|
||||
public Token getCloseToken() {
|
||||
return this.closeToken;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import org.codehaus.groovy.ast.stmt.Statement;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
@Deprecated
|
||||
public interface AppendOrAddStatementFactory {
|
||||
|
||||
enum Action {
|
||||
|
@ -0,0 +1,10 @@
|
||||
package groowt.view.component.web.transpile;
|
||||
|
||||
import groowt.view.component.web.ast.node.BodyTextNode;
|
||||
import org.codehaus.groovy.ast.stmt.Statement;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface BodyTextTranspiler {
|
||||
List<Statement> createBodyTextStatements(BodyTextNode bodyTextNode, TranspilerState state);
|
||||
}
|
@ -6,8 +6,5 @@ import org.codehaus.groovy.ast.stmt.Statement;
|
||||
import java.util.List;
|
||||
|
||||
public interface ComponentTranspiler {
|
||||
List<Statement> createComponentStatements(
|
||||
ComponentNode componentNode,
|
||||
TranspilerState state
|
||||
);
|
||||
List<Statement> createComponentStatements(ComponentNode componentNode, TranspilerState state);
|
||||
}
|
||||
|
@ -0,0 +1,107 @@
|
||||
package groowt.view.component.web.transpile;
|
||||
|
||||
import groowt.util.di.annotation.Given;
|
||||
import groowt.view.component.web.WebViewComponentBugError;
|
||||
import groowt.view.component.web.ast.node.*;
|
||||
import jakarta.inject.Inject;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.codehaus.groovy.ast.expr.ConstantExpression;
|
||||
import org.codehaus.groovy.ast.stmt.Statement;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static groowt.view.component.web.transpile.TranspilerUtil.getStringLiteral;
|
||||
|
||||
public class DefaultBodyTextTranspiler implements BodyTextTranspiler {
|
||||
|
||||
private final GroovyBodyNodeTranspiler groovyBodyNodeTranspiler;
|
||||
private final PositionSetter positionSetter;
|
||||
private final LeftShiftFactory leftShiftFactory;
|
||||
private final boolean includeComments;
|
||||
|
||||
@Inject
|
||||
public DefaultBodyTextTranspiler(
|
||||
GroovyBodyNodeTranspiler groovyBodyNodeTranspiler,
|
||||
PositionSetter positionSetter,
|
||||
LeftShiftFactory leftShiftFactory,
|
||||
@Given boolean includeComments
|
||||
) {
|
||||
this.groovyBodyNodeTranspiler = groovyBodyNodeTranspiler;
|
||||
this.positionSetter = positionSetter;
|
||||
this.leftShiftFactory = leftShiftFactory;
|
||||
this.includeComments = includeComments;
|
||||
}
|
||||
|
||||
protected Statement handleStringLiteral(Token source) {
|
||||
final ConstantExpression literal = getStringLiteral(source.getText());
|
||||
this.positionSetter.setPosition(literal, source);
|
||||
return this.leftShiftFactory.create(literal);
|
||||
}
|
||||
|
||||
protected Statement handleStringLiteral(Node source, String content) {
|
||||
final ConstantExpression literal = getStringLiteral(content);
|
||||
this.positionSetter.setPosition(literal, source);
|
||||
return this.leftShiftFactory.create(literal);
|
||||
}
|
||||
|
||||
protected List<Statement> handleHtmlCommentChild(HtmlCommentChild child, TranspilerState state) {
|
||||
return switch (child) {
|
||||
case BodyTextChild bodyTextChild -> this.handleBodyTextChild(bodyTextChild, state);
|
||||
default -> throw new WebViewComponentBugError(new UnsupportedOperationException(
|
||||
"Unsupported HtmlCommentChild type " + child.getClass().getName()
|
||||
));
|
||||
};
|
||||
}
|
||||
|
||||
protected List<Statement> handleQuestionTagChild(QuestionTagChild child, TranspilerState state) {
|
||||
return switch (child) {
|
||||
case BodyTextChild bodyTextChild -> this.handleBodyTextChild(bodyTextChild, state);
|
||||
default -> throw new WebViewComponentBugError(new UnsupportedOperationException(
|
||||
"Unsupported QuestionTagChild type " + child.getClass().getName()
|
||||
));
|
||||
};
|
||||
}
|
||||
|
||||
protected List<Statement> handleBodyTextChild(BodyTextChild child, TranspilerState state) {
|
||||
final List<Statement> result = new ArrayList<>();
|
||||
switch (child) {
|
||||
case QuestionNode questionNode -> {
|
||||
result.add(this.handleStringLiteral(questionNode.getOpenToken()));
|
||||
questionNode.getChildrenAsQuestionTagChildren().stream()
|
||||
.map(questionChild -> this.handleQuestionTagChild(questionChild, state))
|
||||
.forEach(result::addAll);
|
||||
result.add(this.handleStringLiteral(questionNode.getCloseToken()));
|
||||
}
|
||||
case HtmlCommentNode commentNode -> {
|
||||
if (this.includeComments) {
|
||||
result.add(this.handleStringLiteral(commentNode.getOpenToken()));
|
||||
commentNode.getChildrenAsHtmlCommentChildren().stream()
|
||||
.map(commentChild -> this.handleHtmlCommentChild(commentChild, state))
|
||||
.forEach(result::addAll);
|
||||
result.add(this.handleStringLiteral(commentNode.getCloseToken()));
|
||||
}
|
||||
}
|
||||
case TextNode textNode -> {
|
||||
result.add(this.handleStringLiteral(textNode, textNode.getContent()));
|
||||
}
|
||||
case GroovyBodyNode groovyBodyNode -> {
|
||||
result.add(this.groovyBodyNodeTranspiler.createGroovyBodyNodeStatements(groovyBodyNode, state));
|
||||
}
|
||||
default -> throw new WebViewComponentBugError(new UnsupportedOperationException(
|
||||
"BodyTextChild of type " + child.getClass().getName() + " is not supported."
|
||||
));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Statement> createBodyTextStatements(BodyTextNode bodyTextNode, TranspilerState state) {
|
||||
final List<Statement> result = new ArrayList<>();
|
||||
for (final BodyTextChild child : bodyTextNode.getChildrenAsBodyTextChildren()) {
|
||||
result.addAll(this.handleBodyTextChild(child, state));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
@ -1,26 +1,20 @@
|
||||
package groowt.view.component.web.transpile;
|
||||
|
||||
import groowt.view.component.web.WebViewComponentBugError;
|
||||
import groowt.view.component.web.ast.node.*;
|
||||
import jakarta.inject.Inject;
|
||||
import org.codehaus.groovy.ast.expr.GStringExpression;
|
||||
import groowt.view.component.web.ast.node.BodyNode;
|
||||
import groowt.view.component.web.ast.node.BodyTextNode;
|
||||
import groowt.view.component.web.ast.node.ComponentNode;
|
||||
import groowt.view.component.web.ast.node.Node;
|
||||
import org.codehaus.groovy.ast.stmt.BlockStatement;
|
||||
|
||||
public class DefaultBodyTranspiler implements BodyTranspiler {
|
||||
|
||||
private final GStringTranspiler gStringTranspiler;
|
||||
private final JStringTranspiler jStringTranspiler;
|
||||
private final ComponentTranspiler componentTranspiler;
|
||||
private final BodyTextTranspiler bodyTextTranspiler;
|
||||
|
||||
@Inject
|
||||
public DefaultBodyTranspiler(
|
||||
GStringTranspiler gStringTranspiler,
|
||||
JStringTranspiler jStringTranspiler,
|
||||
ComponentTranspiler componentTranspiler
|
||||
) {
|
||||
this.gStringTranspiler = gStringTranspiler;
|
||||
this.jStringTranspiler = jStringTranspiler;
|
||||
public DefaultBodyTranspiler(ComponentTranspiler componentTranspiler, BodyTextTranspiler bodyTextTranspiler) {
|
||||
this.componentTranspiler = componentTranspiler;
|
||||
this.bodyTextTranspiler = bodyTextTranspiler;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -33,27 +27,10 @@ public class DefaultBodyTranspiler implements BodyTranspiler {
|
||||
block.setVariableScope(state.pushScope());
|
||||
for (final Node child : bodyNode.getChildren()) {
|
||||
switch (child) {
|
||||
case GStringBodyTextNode gStringBodyTextNode -> {
|
||||
final GStringExpression gString = this.gStringTranspiler.createGStringExpression(
|
||||
gStringBodyTextNode
|
||||
);
|
||||
block.addStatement(addOrAppendCallback.createStatement(gStringBodyTextNode, gString));
|
||||
}
|
||||
case JStringBodyTextNode jStringBodyTextNode -> {
|
||||
block.addStatement(
|
||||
addOrAppendCallback.createStatement(
|
||||
jStringBodyTextNode,
|
||||
this.jStringTranspiler.createStringLiteral(jStringBodyTextNode)
|
||||
)
|
||||
);
|
||||
}
|
||||
case ComponentNode componentNode -> {
|
||||
// DO NOT add/append this, because the component transpiler does it already
|
||||
case ComponentNode componentNode ->
|
||||
block.addStatements(this.componentTranspiler.createComponentStatements(componentNode, state));
|
||||
}
|
||||
case PlainScriptletNode plainScriptletNode -> {
|
||||
throw new UnsupportedOperationException("TODO");
|
||||
}
|
||||
case BodyTextNode bodyTextNode ->
|
||||
block.addStatements(this.bodyTextTranspiler.createBodyTextStatements(bodyTextNode, state));
|
||||
default -> throw new WebViewComponentBugError(new UnsupportedOperationException(
|
||||
"BodyNode child of type " + child.getClass().getSimpleName() + " is not supported."
|
||||
));
|
||||
|
@ -0,0 +1,132 @@
|
||||
package groowt.view.component.web.transpile;
|
||||
|
||||
import groowt.view.component.web.WebViewComponentBugError;
|
||||
import groowt.view.component.web.ast.node.*;
|
||||
import groowt.view.component.web.transpile.groovy.GroovyUtil;
|
||||
import jakarta.inject.Inject;
|
||||
import org.codehaus.groovy.ast.expr.*;
|
||||
import org.codehaus.groovy.ast.stmt.BlockStatement;
|
||||
import org.codehaus.groovy.ast.stmt.ExpressionStatement;
|
||||
import org.codehaus.groovy.ast.stmt.Statement;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DefaultGroovyBodyNodeTranspiler implements GroovyBodyNodeTranspiler {
|
||||
|
||||
private final PositionSetter positionSetter;
|
||||
private final LeftShiftFactory leftShiftFactory;
|
||||
|
||||
@Inject
|
||||
public DefaultGroovyBodyNodeTranspiler(PositionSetter positionSetter, LeftShiftFactory leftShiftFactory) {
|
||||
this.positionSetter = positionSetter;
|
||||
this.leftShiftFactory = leftShiftFactory;
|
||||
}
|
||||
|
||||
protected ClosureExpression convertToClosure(Node node, String source) {
|
||||
final GroovyUtil.ConvertResult convertResult = GroovyUtil.convert(
|
||||
"def cl = {" + source + "}"
|
||||
);
|
||||
final BlockStatement convertBlock = convertResult.blockStatement();
|
||||
|
||||
if (convertBlock == null) {
|
||||
throw new WebViewComponentBugError("Did not expect convertBlock to be null.");
|
||||
}
|
||||
if (convertBlock.isEmpty()) {
|
||||
throw new WebViewComponentBugError("Did not expect convertBlock to be empty.");
|
||||
}
|
||||
|
||||
final ExpressionStatement clStmt = (ExpressionStatement) convertBlock.getStatements().getFirst();
|
||||
final BinaryExpression clAssign = (BinaryExpression) clStmt.getExpression();
|
||||
final ClosureExpression cl = (ClosureExpression) clAssign.getRightExpression();
|
||||
|
||||
final PositionVisitor positionVisitor = new PositionVisitor(
|
||||
this.positionSetter.withOffset(0, -10), node
|
||||
);
|
||||
cl.visit(positionVisitor);
|
||||
|
||||
return cl;
|
||||
}
|
||||
|
||||
protected Statement handleEqualsScriptlet(EqualsScriptletNode equalsScriptletNode, TranspilerState state) {
|
||||
final ClosureExpression cl = this.convertToClosure(equalsScriptletNode, equalsScriptletNode.getGroovyCode());
|
||||
final MethodCallExpression callExpr;
|
||||
if (cl.getParameters() == null) {
|
||||
callExpr = new MethodCallExpression(cl, "call", EmptyExpression.INSTANCE);
|
||||
} else {
|
||||
final ArgumentListExpression argsList = new ArgumentListExpression(
|
||||
List.of(state.hasCurrentChildList() ? state.getCurrentChildList() : state.getWriter())
|
||||
);
|
||||
callExpr = new MethodCallExpression(cl, "call", argsList);
|
||||
}
|
||||
return this.leftShiftFactory.create(callExpr);
|
||||
}
|
||||
|
||||
protected Statement handlePlainScriptlet(PlainScriptletNode plainScriptletNode, TranspilerState state) {
|
||||
final ClosureExpression cl = this.convertToClosure(plainScriptletNode, plainScriptletNode.getGroovyCode());
|
||||
final MethodCallExpression callExpr;
|
||||
if (cl.getParameters() == null) {
|
||||
callExpr = new MethodCallExpression(cl, "call", EmptyExpression.INSTANCE);
|
||||
} else {
|
||||
final ArgumentListExpression argsList = new ArgumentListExpression(
|
||||
List.of(state.hasCurrentChildList() ? state.getCurrentChildList() : state.getWriter())
|
||||
);
|
||||
callExpr = new MethodCallExpression(cl, "call", argsList);
|
||||
}
|
||||
|
||||
return new ExpressionStatement(callExpr);
|
||||
}
|
||||
|
||||
protected Statement handleDollarScriptlet(DollarScriptletNode dollarScriptletNode) {
|
||||
final ClosureExpression cl = this.convertToClosure(dollarScriptletNode, dollarScriptletNode.getGroovyCode());
|
||||
final Expression toLeftShift;
|
||||
if (cl.getParameters() == null) {
|
||||
toLeftShift = cl;
|
||||
} else {
|
||||
final Statement stmt = cl.getCode();
|
||||
if (stmt instanceof ExpressionStatement exprStmt) {
|
||||
toLeftShift = exprStmt.getExpression();
|
||||
} else {
|
||||
toLeftShift = cl;
|
||||
}
|
||||
}
|
||||
return this.leftShiftFactory.create(toLeftShift);
|
||||
}
|
||||
|
||||
protected Statement handleDollarReference(DollarReferenceNode dollarReferenceNode) {
|
||||
VariableExpression root = null;
|
||||
PropertyExpression propertyExpr = null;
|
||||
for (final String part : dollarReferenceNode.getParts()) {
|
||||
if (root == null) {
|
||||
root = new VariableExpression(part);
|
||||
} else if (propertyExpr == null) {
|
||||
propertyExpr = new PropertyExpression(root, part);
|
||||
} else {
|
||||
propertyExpr = new PropertyExpression(propertyExpr, part);
|
||||
}
|
||||
}
|
||||
final var positionVisitor = new PositionVisitor(this.positionSetter, dollarReferenceNode);
|
||||
if (propertyExpr != null) {
|
||||
propertyExpr.visit(positionVisitor);
|
||||
return this.leftShiftFactory.create(propertyExpr);
|
||||
} else if (root != null) {
|
||||
root.visit(positionVisitor);
|
||||
return this.leftShiftFactory.create(root);
|
||||
} else {
|
||||
throw new WebViewComponentBugError("Did not expect root to be null.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement createGroovyBodyNodeStatements(GroovyBodyNode groovyBodyNode, TranspilerState state) {
|
||||
return switch (groovyBodyNode) {
|
||||
case EqualsScriptletNode equalsScriptletNode -> this.handleEqualsScriptlet(equalsScriptletNode, state);
|
||||
case PlainScriptletNode plainScriptletNode -> this.handlePlainScriptlet(plainScriptletNode, state);
|
||||
case DollarScriptletNode dollarScriptletNode -> this.handleDollarScriptlet(dollarScriptletNode);
|
||||
case DollarReferenceNode dollarReferenceNode -> this.handleDollarReference(dollarReferenceNode);
|
||||
default -> throw new WebViewComponentBugError(new UnsupportedOperationException(
|
||||
"GroovyBodyNode of type " + groovyBodyNode.getClass().getName() + " is not supported."
|
||||
));
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -3,12 +3,11 @@ package groowt.view.component.web.transpile;
|
||||
import groowt.view.component.web.ast.node.JStringBodyTextNode;
|
||||
import groowt.view.component.web.ast.node.JStringValueNode;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Singleton;
|
||||
import org.codehaus.groovy.ast.expr.ConstantExpression;
|
||||
|
||||
import static org.apache.groovy.parser.antlr4.util.StringUtils.*;
|
||||
|
||||
@Singleton
|
||||
@Deprecated
|
||||
public class DefaultJStringTranspiler implements JStringTranspiler {
|
||||
|
||||
private final PositionSetter positionSetter;
|
||||
|
@ -28,7 +28,7 @@ public class DefaultTranspilerConfiguration implements TranspilerConfiguration {
|
||||
DefaultProvider.ofLazy(BodyTranspiler.class, this::getBodyTranspiler)
|
||||
);
|
||||
this.valueNodeTranspiler = new DefaultValueNodeTranspiler(componentTranspiler);
|
||||
this.bodyTranspiler = new DefaultBodyTranspiler(gStringTranspiler, jStringTranspiler, componentTranspiler);
|
||||
this.bodyTranspiler = new DefaultBodyTranspiler(componentTranspiler, null); // TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,8 @@
|
||||
package groowt.view.component.web.transpile;
|
||||
|
||||
import groowt.view.component.web.ast.node.GroovyBodyNode;
|
||||
import org.codehaus.groovy.ast.stmt.Statement;
|
||||
|
||||
public interface GroovyBodyNodeTranspiler {
|
||||
Statement createGroovyBodyNodeStatements(GroovyBodyNode groovyBodyNode, TranspilerState state);
|
||||
}
|
@ -4,6 +4,7 @@ import groowt.view.component.web.ast.node.JStringBodyTextNode;
|
||||
import groowt.view.component.web.ast.node.JStringValueNode;
|
||||
import org.codehaus.groovy.ast.expr.ConstantExpression;
|
||||
|
||||
@Deprecated
|
||||
public interface JStringTranspiler {
|
||||
ConstantExpression createStringLiteral(JStringBodyTextNode bodyTextNode);
|
||||
ConstantExpression createStringLiteral(JStringValueNode jStringValueNode);
|
||||
|
@ -0,0 +1,8 @@
|
||||
package groowt.view.component.web.transpile;
|
||||
|
||||
import org.codehaus.groovy.ast.expr.Expression;
|
||||
import org.codehaus.groovy.ast.stmt.Statement;
|
||||
|
||||
public interface LeftShiftFactory {
|
||||
Statement create(Expression rightSide);
|
||||
}
|
@ -2,6 +2,7 @@ package groowt.view.component.web.transpile;
|
||||
|
||||
import groowt.view.component.web.ast.node.Node;
|
||||
import groowt.view.component.web.util.TokenRange;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.codehaus.groovy.ast.ASTNode;
|
||||
|
||||
public interface PositionSetter {
|
||||
@ -24,8 +25,17 @@ public interface PositionSetter {
|
||||
*/
|
||||
void setPositionOffsetInContainer(ASTNode target, Node container);
|
||||
|
||||
void setPosition(ASTNode target, Token source);
|
||||
|
||||
void setPosition(ASTNode target, TokenRange tokenRange);
|
||||
void setPosition(ASTNode target, Node source);
|
||||
|
||||
@Deprecated
|
||||
void setPosition(ASTNode target, Node start, Node end);
|
||||
|
||||
@Deprecated
|
||||
void setToStartOf(ASTNode target, Node source);
|
||||
|
||||
PositionSetter withOffset(int lineOffset, int columnOffset);
|
||||
|
||||
}
|
||||
|
@ -3,18 +3,35 @@ package groowt.view.component.web.transpile;
|
||||
import groowt.view.component.web.ast.node.Node;
|
||||
import groowt.view.component.web.util.SourcePosition;
|
||||
import groowt.view.component.web.util.TokenRange;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.codehaus.groovy.ast.ASTNode;
|
||||
|
||||
import static groowt.view.component.web.util.SourcePosition.fromEndOfToken;
|
||||
import static groowt.view.component.web.util.SourcePosition.fromStartOfToken;
|
||||
|
||||
public class SimplePositionSetter implements PositionSetter {
|
||||
|
||||
protected void set(ASTNode target, int startLine, int startColumn, int endLine, int endColumn) {
|
||||
target.setLineNumber(startLine);
|
||||
target.setColumnNumber(startColumn);
|
||||
target.setLastLineNumber(endLine);
|
||||
target.setLastColumnNumber(endColumn);
|
||||
private final int lineOffset;
|
||||
private final int columnOffset;
|
||||
|
||||
public SimplePositionSetter(int lineOffset, int columnOffset) {
|
||||
this.lineOffset = lineOffset;
|
||||
this.columnOffset = columnOffset;
|
||||
}
|
||||
|
||||
protected void set(ASTNode target, SourcePosition start, SourcePosition end) {
|
||||
public SimplePositionSetter() {
|
||||
this.lineOffset = 0;
|
||||
this.columnOffset = 0;
|
||||
}
|
||||
|
||||
protected void set(ASTNode target, int startLine, int startColumn, int endLine, int endColumn) {
|
||||
target.setLineNumber(startLine + this.lineOffset);
|
||||
target.setColumnNumber(startColumn + this.columnOffset);
|
||||
target.setLastLineNumber(endLine + this.lineOffset);
|
||||
target.setLastColumnNumber(endColumn + this.columnOffset);
|
||||
}
|
||||
|
||||
protected final void set(ASTNode target, SourcePosition start, SourcePosition end) {
|
||||
this.set(target, start.line(), start.column(), end.line(), end.column());
|
||||
}
|
||||
|
||||
@ -36,6 +53,11 @@ public class SimplePositionSetter implements PositionSetter {
|
||||
this.set(target, startPosition, endPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPosition(ASTNode target, Token source) {
|
||||
this.set(target, fromStartOfToken(source), fromEndOfToken(source));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPosition(ASTNode target, TokenRange tokenRange) {
|
||||
this.set(target, tokenRange.getStartPosition(), tokenRange.getEndPosition());
|
||||
@ -49,11 +71,8 @@ public class SimplePositionSetter implements PositionSetter {
|
||||
@Override
|
||||
public void setPosition(ASTNode target, Node start, Node end) {
|
||||
final var startPosition = start.getTokenRange().getStartPosition();
|
||||
target.setLineNumber(startPosition.line());
|
||||
target.setColumnNumber(startPosition.column());
|
||||
final var endPosition = end.getTokenRange().getEndPosition();
|
||||
target.setLastLineNumber(endPosition.line());
|
||||
target.setLastColumnNumber(endPosition.column());
|
||||
this.set(target, startPosition, endPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -62,4 +81,9 @@ public class SimplePositionSetter implements PositionSetter {
|
||||
this.set(target, tokenRange.getStartPosition(), tokenRange.getStartPosition());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PositionSetter withOffset(int lineOffset, int columnOffset) {
|
||||
return new SimplePositionSetter(lineOffset, columnOffset);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -13,6 +13,8 @@ import org.codehaus.groovy.ast.expr.ConstantExpression;
|
||||
import org.codehaus.groovy.syntax.Token;
|
||||
import org.codehaus.groovy.syntax.Types;
|
||||
|
||||
import static org.apache.groovy.parser.antlr4.util.StringUtils.*;
|
||||
|
||||
public final class TranspilerUtil {
|
||||
|
||||
public static final ClassNode COMPONENT_TEMPLATE = ClassHelper.make(ComponentTemplate.class);
|
||||
@ -39,9 +41,11 @@ public final class TranspilerUtil {
|
||||
}
|
||||
|
||||
public static ConstantExpression getStringLiteral(String content) {
|
||||
final var e = new ConstantExpression(content);
|
||||
e.setNodeMetaData("_IS_STRING", true);
|
||||
return e;
|
||||
final var withoutCR = removeCR(content);
|
||||
final var escaped = replaceEscapes(withoutCR, NONE_SLASHY);
|
||||
final var expr = new ConstantExpression(escaped);
|
||||
expr.setNodeMetaData("_IS_STRING", true);
|
||||
return expr;
|
||||
}
|
||||
|
||||
public static Token getAssignToken() {
|
||||
|
@ -4,10 +4,5 @@ import groowt.view.component.web.ast.node.ValueNode;
|
||||
import org.codehaus.groovy.ast.expr.Expression;
|
||||
|
||||
public interface ValueNodeTranspiler {
|
||||
|
||||
Expression createExpression(
|
||||
ValueNode valueNode,
|
||||
TranspilerState state
|
||||
);
|
||||
|
||||
Expression createExpression(ValueNode valueNode, TranspilerState state);
|
||||
}
|
||||
|
@ -80,15 +80,25 @@ public abstract class NodeFactoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void questionTagNode(@Mock QuestionTagChild child, @Mock TreeNode childAsNode) {
|
||||
public void questionTagNode(
|
||||
@Mock Token open,
|
||||
@Mock Token close,
|
||||
@Mock QuestionTagChild child,
|
||||
@Mock TreeNode childAsNode
|
||||
) {
|
||||
when(child.asNode()).thenReturn(childAsNode);
|
||||
assertNotNull(this.nodeFactory.questionTagNode(this.getTokenRange(), List.of(child)));
|
||||
assertNotNull(this.nodeFactory.questionTagNode(this.getTokenRange(), open, close, List.of(child)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void htmlCommentNode(@Mock HtmlCommentChild child, @Mock TreeNode childAsNode) {
|
||||
public void htmlCommentNode(
|
||||
@Mock Token open,
|
||||
@Mock Token close,
|
||||
@Mock HtmlCommentChild child,
|
||||
@Mock TreeNode childAsNode
|
||||
) {
|
||||
when(child.asNode()).thenReturn(childAsNode);
|
||||
assertNotNull(this.nodeFactory.htmlCommentNode(this.getTokenRange(), List.of(child)));
|
||||
assertNotNull(this.nodeFactory.htmlCommentNode(this.getTokenRange(), open, close, List.of(child)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -218,7 +228,7 @@ public abstract class NodeFactoryTests {
|
||||
|
||||
@Test
|
||||
public void dollarScriptletNode() {
|
||||
assertNotNull(this.nodeFactory.dollarScriptletNode(this.getTokenRange(), 0));
|
||||
assertNotNull(this.nodeFactory.dollarScriptletNode(this.getTokenRange(), ""));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
Loading…
Reference in New Issue
Block a user