Transpiler refactoring finished.
This commit is contained in:
parent
2bac0f55dc
commit
e112d81ea8
@ -361,7 +361,7 @@ public class DefaultAstBuilderVisitor extends WebViewComponentsParserBaseVisitor
|
|||||||
public @Nullable Node visitPlainScriptlet(WebViewComponentsParser.PlainScriptletContext ctx) {
|
public @Nullable Node visitPlainScriptlet(WebViewComponentsParser.PlainScriptletContext ctx) {
|
||||||
final TerminalNode groovyCode = ctx.GroovyCode();
|
final TerminalNode groovyCode = ctx.GroovyCode();
|
||||||
if (groovyCode != null) {
|
if (groovyCode != null) {
|
||||||
return this.nodeFactory.plainScriptletNode(this.getTokenRange(ctx), groovyCode.getSymbol().getTokenIndex());
|
return this.nodeFactory.plainScriptletNode(this.getTokenRange(ctx), groovyCode.getSymbol().getText());
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -381,16 +381,15 @@ public class DefaultAstBuilderVisitor extends WebViewComponentsParserBaseVisitor
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable Node visitDollarReference(WebViewComponentsParser.DollarReferenceContext ctx) {
|
public Node visitDollarReference(WebViewComponentsParser.DollarReferenceContext ctx) {
|
||||||
final TerminalNode groovyCode = ctx.GroovyCode();
|
final String groovyCode = ctx.GroovyCode().getText();
|
||||||
if (groovyCode != null) {
|
final List<String> parts = new ArrayList<>();
|
||||||
return this.nodeFactory.dollarReferenceNode(
|
if (groovyCode.contains(".")) {
|
||||||
this.getTokenRange(ctx),
|
parts.addAll(List.of(groovyCode.split("\\.")));
|
||||||
groovyCode.getSymbol().getTokenIndex()
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
return null;
|
parts.add(groovyCode);
|
||||||
}
|
}
|
||||||
|
return this.nodeFactory.dollarReferenceNode(this.getTokenRange(ctx), parts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -225,8 +225,8 @@ public class DefaultNodeFactory implements NodeFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PlainScriptletNode plainScriptletNode(TokenRange tokenRange, int groovyIndex) {
|
public PlainScriptletNode plainScriptletNode(TokenRange tokenRange, String groovyCode) {
|
||||||
return this.objectFactory.get(PlainScriptletNode.class, tokenRange, groovyIndex);
|
return this.objectFactory.get(PlainScriptletNode.class, tokenRange, groovyCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -235,8 +235,8 @@ public class DefaultNodeFactory implements NodeFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DollarReferenceNode dollarReferenceNode(TokenRange tokenRange, int groovyIndex) {
|
public DollarReferenceNode dollarReferenceNode(TokenRange tokenRange, List<String> parts) {
|
||||||
return this.objectFactory.get(DollarReferenceNode.class, tokenRange, groovyIndex);
|
return this.objectFactory.get(DollarReferenceNode.class, tokenRange, parts);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -76,10 +76,10 @@ public interface NodeFactory {
|
|||||||
|
|
||||||
EqualsScriptletNode equalsScriptletNode(TokenRange tokenRange, String groovyCode);
|
EqualsScriptletNode equalsScriptletNode(TokenRange tokenRange, String groovyCode);
|
||||||
|
|
||||||
PlainScriptletNode plainScriptletNode(TokenRange tokenRange, int groovyIndex);
|
PlainScriptletNode plainScriptletNode(TokenRange tokenRange, String groovyCode);
|
||||||
|
|
||||||
DollarScriptletNode dollarScriptletNode(TokenRange tokenRange, String groovyCode);
|
DollarScriptletNode dollarScriptletNode(TokenRange tokenRange, String groovyCode);
|
||||||
|
|
||||||
DollarReferenceNode dollarReferenceNode(TokenRange tokenRange, int groovyIndex);
|
DollarReferenceNode dollarReferenceNode(TokenRange tokenRange, List<String> parts);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public class GroovyCodeNodeExtension implements NodeExtension {
|
public class GroovyCodeNodeExtension implements NodeExtension {
|
||||||
|
|
||||||
private final Node self;
|
private final Node self;
|
||||||
|
@ -38,7 +38,7 @@ public class ClosureValueNode extends AbstractLeafNode implements ValueNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected String toValidGroovyCode(List<Token> groovyTokens) {
|
protected String toValidGroovyCode(List<Token> groovyTokens) {
|
||||||
return "def c = { " + groovyTokens.stream().map(Token::getText).collect(Collectors.joining()) + "\n}";
|
return "def cl = {" + groovyTokens.stream().map(Token::getText).collect(Collectors.joining()) + "}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public GroovyCodeNodeExtension getGroovyCode() {
|
public GroovyCodeNodeExtension getGroovyCode() {
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
package groowt.view.component.web.ast.node;
|
|
||||||
|
|
||||||
import groowt.util.di.annotation.Given;
|
|
||||||
import groowt.view.component.web.ast.extension.GStringNodeExtension;
|
|
||||||
import groowt.view.component.web.ast.extension.NodeExtensionContainer;
|
|
||||||
import groowt.view.component.web.util.TokenRange;
|
|
||||||
import jakarta.inject.Inject;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public class GStringBodyTextNode extends AbstractTreeNode implements BodyChildNode {
|
|
||||||
|
|
||||||
protected static List<? extends Node> checkChildren(List<? extends Node> children) {
|
|
||||||
for (final var child : children) {
|
|
||||||
if (!(child instanceof JStringBodyTextNode || child.hasExtension(GStringNodeExtension.class))) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"Children of GStringBodyTextNode must be either a JStringBodyTextNode, " +
|
|
||||||
"or have a GStringNodeExtension."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return children;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public GStringBodyTextNode(
|
|
||||||
NodeExtensionContainer extensionContainer,
|
|
||||||
@Given TokenRange tokenRange,
|
|
||||||
@Given List<? extends Node> children
|
|
||||||
) {
|
|
||||||
super(tokenRange, extensionContainer, checkChildren(children));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
package groowt.view.component.web.ast.node;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public class JStringBodyTextNode extends AbstractLeafNode implements BodyChildNode {
|
|
||||||
|
|
||||||
private final String content;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public JStringBodyTextNode(
|
|
||||||
NodeExtensionContainer extensionContainer,
|
|
||||||
@Given TokenRange tokenRange,
|
|
||||||
@Given String content
|
|
||||||
) {
|
|
||||||
super(tokenRange, extensionContainer);
|
|
||||||
this.content = content;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getContent() {
|
|
||||||
return this.content;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
package groowt.view.component.web.transpile;
|
|
||||||
|
|
||||||
import groowt.view.component.web.ast.node.BodyChildNode;
|
|
||||||
import org.codehaus.groovy.ast.expr.Expression;
|
|
||||||
import org.codehaus.groovy.ast.stmt.Statement;
|
|
||||||
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public interface AppendOrAddStatementFactory {
|
|
||||||
|
|
||||||
enum Action {
|
|
||||||
ADD, APPEND
|
|
||||||
}
|
|
||||||
|
|
||||||
Statement addOrAppend(BodyChildNode sourceNode, TranspilerState state, Function<Action, Expression> getRightSide);
|
|
||||||
|
|
||||||
default Statement addOrAppend(BodyChildNode sourceNode, TranspilerState state, Expression rightSide) {
|
|
||||||
return this.addOrAppend(sourceNode, state, ignored -> rightSide);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,22 +1,8 @@
|
|||||||
package groowt.view.component.web.transpile;
|
package groowt.view.component.web.transpile;
|
||||||
|
|
||||||
import groowt.view.component.web.ast.node.BodyChildNode;
|
|
||||||
import groowt.view.component.web.ast.node.BodyNode;
|
import groowt.view.component.web.ast.node.BodyNode;
|
||||||
import org.codehaus.groovy.ast.expr.Expression;
|
|
||||||
import org.codehaus.groovy.ast.stmt.BlockStatement;
|
import org.codehaus.groovy.ast.stmt.BlockStatement;
|
||||||
import org.codehaus.groovy.ast.stmt.Statement;
|
|
||||||
|
|
||||||
public interface BodyTranspiler {
|
public interface BodyTranspiler {
|
||||||
|
BlockStatement transpileBody(BodyNode bodyNode, TranspilerState state);
|
||||||
@FunctionalInterface
|
|
||||||
interface AddOrAppendCallback {
|
|
||||||
Statement createStatement(BodyChildNode source, Expression expression);
|
|
||||||
}
|
|
||||||
|
|
||||||
BlockStatement transpileBody(
|
|
||||||
BodyNode bodyNode,
|
|
||||||
AddOrAppendCallback addOrAppendCallback,
|
|
||||||
TranspilerState state
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,79 +0,0 @@
|
|||||||
package groowt.view.component.web.transpile;
|
|
||||||
|
|
||||||
import groovy.lang.Tuple2;
|
|
||||||
import groowt.view.component.web.ast.node.BodyChildNode;
|
|
||||||
import org.codehaus.groovy.ast.expr.*;
|
|
||||||
import org.codehaus.groovy.ast.stmt.ExpressionStatement;
|
|
||||||
import org.codehaus.groovy.ast.stmt.Statement;
|
|
||||||
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
public class DefaultAppendOrAddStatementFactory implements AppendOrAddStatementFactory {
|
|
||||||
|
|
||||||
private void addLineAndColumn(
|
|
||||||
BodyChildNode bodyChildNode,
|
|
||||||
TupleExpression args
|
|
||||||
) {
|
|
||||||
final Tuple2<ConstantExpression, ConstantExpression> lineAndColumn = TranspilerUtil.lineAndColumn(
|
|
||||||
bodyChildNode.asNode().getTokenRange().getStartPosition()
|
|
||||||
);
|
|
||||||
args.addExpression(lineAndColumn.getV1());
|
|
||||||
args.addExpression(lineAndColumn.getV2());
|
|
||||||
}
|
|
||||||
|
|
||||||
private Statement doCreate(
|
|
||||||
BodyChildNode bodyChildNode,
|
|
||||||
Expression rightSide,
|
|
||||||
VariableExpression target,
|
|
||||||
String methodName // ,
|
|
||||||
// boolean addLineAndColumn
|
|
||||||
) {
|
|
||||||
final ArgumentListExpression args;
|
|
||||||
if (rightSide instanceof ArgumentListExpression argumentListExpression) {
|
|
||||||
args = argumentListExpression;
|
|
||||||
} else {
|
|
||||||
args = new ArgumentListExpression();
|
|
||||||
args.addExpression(rightSide);
|
|
||||||
}
|
|
||||||
// if (addLineAndColumn &&
|
|
||||||
// NodeUtil.isAnyOfType(bodyChildNode.asNode(), GStringBodyTextNode.class, ComponentNode.class)) {
|
|
||||||
// this.addLineAndColumn(bodyChildNode, args);
|
|
||||||
// }
|
|
||||||
final MethodCallExpression outExpression = new MethodCallExpression(target, methodName, args);
|
|
||||||
return new ExpressionStatement(outExpression);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Statement addOnly(BodyChildNode bodyChildNode, TranspilerState state, Expression rightSide) {
|
|
||||||
return this.doCreate(
|
|
||||||
bodyChildNode,
|
|
||||||
rightSide,
|
|
||||||
state.getCurrentChildList(),
|
|
||||||
TranspilerUtil.ADD //,
|
|
||||||
// false
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Statement appendOnly(BodyChildNode bodyChildNode, TranspilerState state, Expression rightSide) {
|
|
||||||
return this.doCreate(
|
|
||||||
bodyChildNode,
|
|
||||||
rightSide,
|
|
||||||
state.getWriter(),
|
|
||||||
TranspilerUtil.APPEND //,
|
|
||||||
// false
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Statement addOrAppend(
|
|
||||||
BodyChildNode bodyChildNode,
|
|
||||||
TranspilerState state,
|
|
||||||
Function<Action, Expression> getRightSide
|
|
||||||
) {
|
|
||||||
if (state.hasCurrentChildList()) {
|
|
||||||
return this.addOnly(bodyChildNode, state, getRightSide.apply(Action.ADD));
|
|
||||||
} else {
|
|
||||||
return this.appendOnly(bodyChildNode, state, getRightSide.apply(Action.APPEND));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -33,16 +33,16 @@ public class DefaultBodyTextTranspiler implements BodyTextTranspiler {
|
|||||||
this.includeComments = includeComments;
|
this.includeComments = includeComments;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Statement handleStringLiteral(Token source) {
|
protected Statement handleStringLiteral(TranspilerState state, Token source) {
|
||||||
final ConstantExpression literal = getStringLiteral(source.getText());
|
final ConstantExpression literal = getStringLiteral(source.getText());
|
||||||
this.positionSetter.setPosition(literal, source);
|
this.positionSetter.setPosition(literal, source);
|
||||||
return this.leftShiftFactory.create(literal);
|
return this.leftShiftFactory.create(state, literal);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Statement handleStringLiteral(Node source, String content) {
|
protected Statement handleStringLiteral(TranspilerState state, Node source, String content) {
|
||||||
final ConstantExpression literal = getStringLiteral(content);
|
final ConstantExpression literal = getStringLiteral(content);
|
||||||
this.positionSetter.setPosition(literal, source);
|
this.positionSetter.setPosition(literal, source);
|
||||||
return this.leftShiftFactory.create(literal);
|
return this.leftShiftFactory.create(state, literal);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected List<Statement> handleHtmlCommentChild(HtmlCommentChild child, TranspilerState state) {
|
protected List<Statement> handleHtmlCommentChild(HtmlCommentChild child, TranspilerState state) {
|
||||||
@ -67,23 +67,23 @@ public class DefaultBodyTextTranspiler implements BodyTextTranspiler {
|
|||||||
final List<Statement> result = new ArrayList<>();
|
final List<Statement> result = new ArrayList<>();
|
||||||
switch (child) {
|
switch (child) {
|
||||||
case QuestionNode questionNode -> {
|
case QuestionNode questionNode -> {
|
||||||
result.add(this.handleStringLiteral(questionNode.getOpenToken()));
|
result.add(this.handleStringLiteral(state, questionNode.getOpenToken()));
|
||||||
questionNode.getChildrenAsQuestionTagChildren().stream()
|
questionNode.getChildrenAsQuestionTagChildren().stream()
|
||||||
.map(questionChild -> this.handleQuestionTagChild(questionChild, state))
|
.map(questionChild -> this.handleQuestionTagChild(questionChild, state))
|
||||||
.forEach(result::addAll);
|
.forEach(result::addAll);
|
||||||
result.add(this.handleStringLiteral(questionNode.getCloseToken()));
|
result.add(this.handleStringLiteral(state, questionNode.getCloseToken()));
|
||||||
}
|
}
|
||||||
case HtmlCommentNode commentNode -> {
|
case HtmlCommentNode commentNode -> {
|
||||||
if (this.includeComments) {
|
if (this.includeComments) {
|
||||||
result.add(this.handleStringLiteral(commentNode.getOpenToken()));
|
result.add(this.handleStringLiteral(state, commentNode.getOpenToken()));
|
||||||
commentNode.getChildrenAsHtmlCommentChildren().stream()
|
commentNode.getChildrenAsHtmlCommentChildren().stream()
|
||||||
.map(commentChild -> this.handleHtmlCommentChild(commentChild, state))
|
.map(commentChild -> this.handleHtmlCommentChild(commentChild, state))
|
||||||
.forEach(result::addAll);
|
.forEach(result::addAll);
|
||||||
result.add(this.handleStringLiteral(commentNode.getCloseToken()));
|
result.add(this.handleStringLiteral(state, commentNode.getCloseToken()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case TextNode textNode -> {
|
case TextNode textNode -> {
|
||||||
result.add(this.handleStringLiteral(textNode, textNode.getContent()));
|
result.add(this.handleStringLiteral(state, textNode, textNode.getContent()));
|
||||||
}
|
}
|
||||||
case GroovyBodyNode groovyBodyNode -> {
|
case GroovyBodyNode groovyBodyNode -> {
|
||||||
result.add(this.groovyBodyNodeTranspiler.createGroovyBodyNodeStatements(groovyBodyNode, state));
|
result.add(this.groovyBodyNodeTranspiler.createGroovyBodyNodeStatements(groovyBodyNode, state));
|
||||||
|
@ -18,11 +18,7 @@ public class DefaultBodyTranspiler implements BodyTranspiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BlockStatement transpileBody(
|
public BlockStatement transpileBody(BodyNode bodyNode, TranspilerState state) {
|
||||||
BodyNode bodyNode,
|
|
||||||
AddOrAppendCallback addOrAppendCallback,
|
|
||||||
TranspilerState state
|
|
||||||
) {
|
|
||||||
final BlockStatement block = new BlockStatement();
|
final BlockStatement block = new BlockStatement();
|
||||||
block.setVariableScope(state.pushScope());
|
block.setVariableScope(state.pushScope());
|
||||||
for (final Node child : bodyNode.getChildren()) {
|
for (final Node child : bodyNode.getChildren()) {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package groowt.view.component.web.transpile;
|
package groowt.view.component.web.transpile;
|
||||||
|
|
||||||
import groowt.util.fp.provider.Provider;
|
|
||||||
import groowt.view.component.context.ComponentResolveException;
|
import groowt.view.component.context.ComponentResolveException;
|
||||||
import groowt.view.component.runtime.ComponentCreateException;
|
import groowt.view.component.runtime.ComponentCreateException;
|
||||||
import groowt.view.component.web.WebViewComponentBugError;
|
import groowt.view.component.web.WebViewComponentBugError;
|
||||||
@ -29,37 +28,25 @@ public class DefaultComponentTranspiler implements ComponentTranspiler {
|
|||||||
private static final Pattern isFqn = Pattern.compile("^(\\p{Ll}.+\\.)+\\p{Lu}.+$");
|
private static final Pattern isFqn = Pattern.compile("^(\\p{Ll}.+\\.)+\\p{Lu}.+$");
|
||||||
private static final Pattern isWithPackage = Pattern.compile("^\\p{Ll}.+\\.");
|
private static final Pattern isWithPackage = Pattern.compile("^\\p{Ll}.+\\.");
|
||||||
|
|
||||||
private final Provider<AppendOrAddStatementFactory> appendOrAddStatementFactoryProvider;
|
private LeftShiftFactory leftShiftFactory;
|
||||||
private final Provider<ComponentClassNodeResolver> componentClassNodeResolverProvider;
|
private ValueNodeTranspiler valueNodeTranspiler;
|
||||||
private final Provider<ValueNodeTranspiler> valueNodeTranspilerProvider;
|
private BodyTranspiler bodyTranspiler;
|
||||||
private final Provider<BodyTranspiler> bodyTranspilerProvider;
|
private ComponentClassNodeResolver componentClassNodeResolver;
|
||||||
|
|
||||||
public DefaultComponentTranspiler(
|
public void setLeftShiftFactory(LeftShiftFactory leftShiftFactory) {
|
||||||
Provider<AppendOrAddStatementFactory> appendOrAddStatementFactoryProvider,
|
this.leftShiftFactory = leftShiftFactory;
|
||||||
Provider<ComponentClassNodeResolver> componentClassNodeResolverProvider,
|
|
||||||
Provider<ValueNodeTranspiler> valueNodeTranspilerProvider,
|
|
||||||
Provider<BodyTranspiler> bodyTranspilerProvider
|
|
||||||
) {
|
|
||||||
this.appendOrAddStatementFactoryProvider = appendOrAddStatementFactoryProvider;
|
|
||||||
this.componentClassNodeResolverProvider = componentClassNodeResolverProvider;
|
|
||||||
this.valueNodeTranspilerProvider = valueNodeTranspilerProvider;
|
|
||||||
this.bodyTranspilerProvider = bodyTranspilerProvider;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ValueNodeTranspiler getValueNodeTranspiler() {
|
public void setValueNodeTranspiler(ValueNodeTranspiler valueNodeTranspiler) {
|
||||||
return this.valueNodeTranspilerProvider.get();
|
this.valueNodeTranspiler = valueNodeTranspiler;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected BodyTranspiler getBodyTranspiler() {
|
public void setBodyTranspiler(BodyTranspiler bodyTranspiler) {
|
||||||
return this.bodyTranspilerProvider.get();
|
this.bodyTranspiler = bodyTranspiler;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AppendOrAddStatementFactory getAppendOrAddStatementFactory() {
|
public void setComponentClassNodeResolver(ComponentClassNodeResolver componentClassNodeResolver) {
|
||||||
return this.appendOrAddStatementFactoryProvider.get();
|
this.componentClassNodeResolver = componentClassNodeResolver;
|
||||||
}
|
|
||||||
|
|
||||||
protected ComponentClassNodeResolver getComponentClassNodeResolver() {
|
|
||||||
return this.componentClassNodeResolverProvider.get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* UTIL */
|
/* UTIL */
|
||||||
@ -104,7 +91,7 @@ public class DefaultComponentTranspiler implements ComponentTranspiler {
|
|||||||
// we need to resolve it
|
// we need to resolve it
|
||||||
final var isWithPackageMatcher = isWithPackage.matcher(identifier);
|
final var isWithPackageMatcher = isWithPackage.matcher(identifier);
|
||||||
if (isWithPackageMatcher.matches()) {
|
if (isWithPackageMatcher.matches()) {
|
||||||
final var resolveResult = this.getComponentClassNodeResolver().getClassForFqn(identifier);
|
final var resolveResult = this.componentClassNodeResolver.getClassForFqn(identifier);
|
||||||
if (resolveResult.isLeft()) {
|
if (resolveResult.isLeft()) {
|
||||||
final var error = resolveResult.getLeft();
|
final var error = resolveResult.getLeft();
|
||||||
error.setNode(componentNode.getArgs().getType());
|
error.setNode(componentNode.getArgs().getType());
|
||||||
@ -117,7 +104,7 @@ public class DefaultComponentTranspiler implements ComponentTranspiler {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
final var resolveResult =
|
final var resolveResult =
|
||||||
this.getComponentClassNodeResolver().getClassForNameWithoutPackage(identifier);
|
this.componentClassNodeResolver.getClassForNameWithoutPackage(identifier);
|
||||||
if (resolveResult.isLeft()) {
|
if (resolveResult.isLeft()) {
|
||||||
final var error = resolveResult.getLeft();
|
final var error = resolveResult.getLeft();
|
||||||
error.setNode(componentNode.getArgs().getType());
|
error.setNode(componentNode.getArgs().getType());
|
||||||
@ -260,7 +247,7 @@ public class DefaultComponentTranspiler implements ComponentTranspiler {
|
|||||||
final Expression valueExpr = switch (attrNode) {
|
final Expression valueExpr = switch (attrNode) {
|
||||||
case BooleanValueAttrNode ignored -> ConstantExpression.PRIM_TRUE;
|
case BooleanValueAttrNode ignored -> ConstantExpression.PRIM_TRUE;
|
||||||
case KeyValueAttrNode keyValueAttrNode ->
|
case KeyValueAttrNode keyValueAttrNode ->
|
||||||
this.getValueNodeTranspiler().createExpression(keyValueAttrNode.getValueNode(), state);
|
this.valueNodeTranspiler.createExpression(keyValueAttrNode.getValueNode(), state);
|
||||||
};
|
};
|
||||||
return new MapEntryExpression(keyExpr, valueExpr);
|
return new MapEntryExpression(keyExpr, valueExpr);
|
||||||
}
|
}
|
||||||
@ -328,11 +315,7 @@ public class DefaultComponentTranspiler implements ComponentTranspiler {
|
|||||||
scope.putDeclaredVariable(childListParam);
|
scope.putDeclaredVariable(childListParam);
|
||||||
state.pushChildList(childListParam);
|
state.pushChildList(childListParam);
|
||||||
|
|
||||||
final BlockStatement bodyStatements = this.getBodyTranspiler().transpileBody(
|
final BlockStatement bodyStatements = this.bodyTranspiler.transpileBody(bodyNode, state);
|
||||||
bodyNode,
|
|
||||||
(sourceNode, expr) -> this.getChildListAdd(childListParam, expr),
|
|
||||||
state
|
|
||||||
);
|
|
||||||
|
|
||||||
// clean up
|
// clean up
|
||||||
state.popChildList();
|
state.popChildList();
|
||||||
@ -491,11 +474,7 @@ public class DefaultComponentTranspiler implements ComponentTranspiler {
|
|||||||
// Create
|
// Create
|
||||||
final List<Statement> createStatements = this.getTypedCreateStatements(typedComponentNode, state);
|
final List<Statement> createStatements = this.getTypedCreateStatements(typedComponentNode, state);
|
||||||
// Append/Add
|
// Append/Add
|
||||||
final Statement addOrAppend = this.getAppendOrAddStatementFactory().addOrAppend(
|
final Statement leftShift = this.leftShiftFactory.create(state, state.getCurrentComponent());
|
||||||
componentNode,
|
|
||||||
state,
|
|
||||||
(VariableExpression) state.getCurrentComponent()
|
|
||||||
);
|
|
||||||
|
|
||||||
// cleanup
|
// cleanup
|
||||||
state.popResolved();
|
state.popResolved();
|
||||||
@ -504,17 +483,16 @@ public class DefaultComponentTranspiler implements ComponentTranspiler {
|
|||||||
final List<Statement> allStatements = new ArrayList<>();
|
final List<Statement> allStatements = new ArrayList<>();
|
||||||
allStatements.addAll(resolveStatements);
|
allStatements.addAll(resolveStatements);
|
||||||
allStatements.addAll(createStatements);
|
allStatements.addAll(createStatements);
|
||||||
allStatements.add(addOrAppend);
|
allStatements.add(leftShift);
|
||||||
|
|
||||||
return allStatements;
|
return allStatements;
|
||||||
} else if (componentNode instanceof FragmentComponentNode fragmentComponentNode) {
|
} else if (componentNode instanceof FragmentComponentNode fragmentComponentNode) {
|
||||||
// Create and add all at once
|
// Create and add all at once
|
||||||
final Statement addOrAppend = this.getAppendOrAddStatementFactory().addOrAppend(
|
final Statement leftShift = this.leftShiftFactory.create(
|
||||||
componentNode,
|
|
||||||
state,
|
state,
|
||||||
this.getFragmentCreateExpression(fragmentComponentNode, state)
|
this.getFragmentCreateExpression(fragmentComponentNode, state)
|
||||||
);
|
);
|
||||||
return List.of(addOrAppend);
|
return List.of(leftShift);
|
||||||
} else {
|
} else {
|
||||||
throw new WebViewComponentBugError(new IllegalArgumentException(
|
throw new WebViewComponentBugError(new IllegalArgumentException(
|
||||||
"Cannot handle a ComponentNode not of type TypedComponentNode or FragmentComponentNode."
|
"Cannot handle a ComponentNode not of type TypedComponentNode or FragmentComponentNode."
|
||||||
|
@ -1,204 +0,0 @@
|
|||||||
package groowt.view.component.web.transpile;
|
|
||||||
|
|
||||||
import groowt.util.fp.option.Option;
|
|
||||||
import groowt.view.component.web.antlr.MergedGroovyCodeToken;
|
|
||||||
import groowt.view.component.web.antlr.WebViewComponentsLexer;
|
|
||||||
import groowt.view.component.web.ast.extension.GStringNodeExtension;
|
|
||||||
import groowt.view.component.web.ast.extension.GStringPathExtension;
|
|
||||||
import groowt.view.component.web.ast.extension.GStringScriptletExtension;
|
|
||||||
import groowt.view.component.web.ast.node.GStringBodyTextNode;
|
|
||||||
import groowt.view.component.web.ast.node.JStringBodyTextNode;
|
|
||||||
import groowt.view.component.web.ast.node.Node;
|
|
||||||
import groowt.view.component.web.transpile.groovy.GroovyUtil;
|
|
||||||
import groowt.view.component.web.util.FilteringIterable;
|
|
||||||
import groowt.view.component.web.util.TokenRange;
|
|
||||||
import org.antlr.v4.runtime.Token;
|
|
||||||
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 org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.ListIterator;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public class DefaultGStringTranspiler implements GStringTranspiler {
|
|
||||||
|
|
||||||
private final PositionSetter positionSetter;
|
|
||||||
private final JStringTranspiler jStringTranspiler;
|
|
||||||
|
|
||||||
public DefaultGStringTranspiler(PositionSetter positionSetter, JStringTranspiler jStringTranspiler) {
|
|
||||||
this.positionSetter = positionSetter;
|
|
||||||
this.jStringTranspiler = jStringTranspiler;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Option<ConstantExpression> checkPrevBeforeDollar(@Nullable Node prev, Node current) {
|
|
||||||
if (!(prev instanceof JStringBodyTextNode)) {
|
|
||||||
return Option.liftLazy(() -> {
|
|
||||||
final ConstantExpression expression = this.jStringTranspiler.createEmptyStringLiteral();
|
|
||||||
this.positionSetter.setToStartOf(expression, current);
|
|
||||||
return expression;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return Option.empty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Option<ConstantExpression> checkNextAfterDollar(@Nullable Node next) {
|
|
||||||
if (next != null && next.hasExtension(GStringNodeExtension.class)) {
|
|
||||||
return Option.liftLazy(() -> {
|
|
||||||
final ConstantExpression expression = this.jStringTranspiler.createEmptyStringLiteral();
|
|
||||||
this.positionSetter.setToStartOf(expression, next);
|
|
||||||
return expression;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return Option.empty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ConstantExpression handleText(JStringBodyTextNode jStringBodyTextNode, @Nullable Node prev) {
|
|
||||||
if (prev instanceof JStringBodyTextNode) {
|
|
||||||
throw new IllegalStateException("Cannot have two texts in a row");
|
|
||||||
}
|
|
||||||
return this.jStringTranspiler.createStringLiteral(jStringBodyTextNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected record PathResult(
|
|
||||||
Expression result,
|
|
||||||
Option<ConstantExpression> before,
|
|
||||||
Option<ConstantExpression> after
|
|
||||||
) {}
|
|
||||||
|
|
||||||
protected PathResult handlePath(Node current, GStringPathExtension path, @Nullable Node prev, @Nullable Node next) {
|
|
||||||
final List<Token> groowtTokens = path.getRawTokens();
|
|
||||||
|
|
||||||
VariableExpression begin = null;
|
|
||||||
PropertyExpression propertyExpression = null;
|
|
||||||
|
|
||||||
for (final Token groowtToken : groowtTokens) {
|
|
||||||
if (groowtToken instanceof MergedGroovyCodeToken groovyCodeToken) {
|
|
||||||
final Iterable<Token> identifierTokenIterable = FilteringIterable.continuingUntilSuccess(
|
|
||||||
groovyCodeToken.getOriginals(),
|
|
||||||
token -> token.getType() == WebViewComponentsLexer.GStringIdentifier
|
|
||||||
);
|
|
||||||
for (final Token identifierToken : identifierTokenIterable) {
|
|
||||||
final String identifier = identifierToken.getText();
|
|
||||||
final TokenRange identifierTokenRange = TokenRange.of(identifierToken);
|
|
||||||
if (begin == null) {
|
|
||||||
begin = new VariableExpression(identifier);
|
|
||||||
this.positionSetter.setPosition(begin, identifierTokenRange);
|
|
||||||
} else if (propertyExpression == null) {
|
|
||||||
propertyExpression = new PropertyExpression(begin, identifier);
|
|
||||||
this.positionSetter.setPosition(propertyExpression, identifierTokenRange);
|
|
||||||
} else {
|
|
||||||
propertyExpression = new PropertyExpression(propertyExpression, identifier);
|
|
||||||
this.positionSetter.setPosition(propertyExpression, identifierTokenRange);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new IllegalStateException("Received a non-MergedGroovyToken from a GStringExtension");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (begin == null) {
|
|
||||||
throw new IllegalStateException("begin is null!");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (propertyExpression != null) {
|
|
||||||
return new PathResult(
|
|
||||||
propertyExpression,
|
|
||||||
this.checkPrevBeforeDollar(prev, current),
|
|
||||||
this.checkNextAfterDollar(next)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return new PathResult(
|
|
||||||
begin,
|
|
||||||
this.checkPrevBeforeDollar(prev, current),
|
|
||||||
this.checkNextAfterDollar(next)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ClosureExpression handleScriptlet(GStringScriptletExtension gStringScriptletExtension) {
|
|
||||||
final GroovyUtil.ConvertResult convertResult = GroovyUtil.convert(
|
|
||||||
"def cl = {" + gStringScriptletExtension.getAsValidEmbeddableCode() + "}"
|
|
||||||
);
|
|
||||||
final BlockStatement convertBlock = convertResult.blockStatement();
|
|
||||||
if (convertBlock == null) {
|
|
||||||
throw new NullPointerException("Did not except convertBlock to be null");
|
|
||||||
}
|
|
||||||
final List<Statement> convertStatements = convertBlock.getStatements();
|
|
||||||
if (convertStatements.size() != 1) {
|
|
||||||
throw new IllegalStateException("Did not expect convertStatements.size() to not equal 1");
|
|
||||||
}
|
|
||||||
final ExpressionStatement convertExpressionStatement = (ExpressionStatement) convertStatements.getFirst();
|
|
||||||
final BinaryExpression assignment = (BinaryExpression) convertExpressionStatement.getExpression();
|
|
||||||
return (ClosureExpression) assignment.getRightExpression();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public GStringExpression createGStringExpression(GStringBodyTextNode gStringBodyTextNode) {
|
|
||||||
final var children = gStringBodyTextNode.getChildren();
|
|
||||||
if (children.isEmpty()) {
|
|
||||||
throw new IllegalArgumentException("Cannot make a gStringOutStatement from zero GStringParts");
|
|
||||||
}
|
|
||||||
|
|
||||||
final String verbatimText = children.stream().map(node -> {
|
|
||||||
if (node instanceof JStringBodyTextNode jStringBodyTextNode) {
|
|
||||||
return jStringBodyTextNode.getContent();
|
|
||||||
} else if (node.hasExtension(GStringNodeExtension.class)) {
|
|
||||||
final var gString = node.getExtension(GStringNodeExtension.class);
|
|
||||||
return switch (gString) {
|
|
||||||
case GStringPathExtension ignored -> gString.getAsValidEmbeddableCode();
|
|
||||||
case GStringScriptletExtension ignored -> "${" + gString.getAsValidEmbeddableCode() + "}";
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"Cannot get verbatim text when one of the given parts has "
|
|
||||||
+ "neither a JStringNodeExtension nor a GStringNodeExtension"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}).collect(Collectors.joining());
|
|
||||||
|
|
||||||
final List<ConstantExpression> texts = new ArrayList<>();
|
|
||||||
final List<Expression> values = new ArrayList<>();
|
|
||||||
final ListIterator<Node> iter = children.listIterator();
|
|
||||||
|
|
||||||
while (iter.hasNext()) {
|
|
||||||
final var prev = iter.previousIndex() > -1 ? children.get(iter.previousIndex()) : null;
|
|
||||||
final var current = iter.next();
|
|
||||||
final var next = iter.nextIndex() < children.size() ? children.get(iter.nextIndex()) : null;
|
|
||||||
if (current instanceof JStringBodyTextNode jStringBodyTextNode) {
|
|
||||||
texts.add(this.handleText(jStringBodyTextNode, prev));
|
|
||||||
} else {
|
|
||||||
switch (current.getExtension(GStringNodeExtension.class)) {
|
|
||||||
case GStringPathExtension path -> {
|
|
||||||
final var pathResult = this.handlePath(current, path, prev, next);
|
|
||||||
pathResult.before().ifPresent(texts::add);
|
|
||||||
values.add(pathResult.result());
|
|
||||||
pathResult.after().ifPresent(texts::add);
|
|
||||||
}
|
|
||||||
case GStringScriptletExtension scriptlet -> {
|
|
||||||
checkPrevBeforeDollar(prev, current).ifPresent(texts::add);
|
|
||||||
values.add(this.handleScriptlet(scriptlet));
|
|
||||||
checkNextAfterDollar(next).ifPresent(texts::add);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(texts.size() == values.size() || texts.size() == values.size() + 1)) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"incorrect amount of texts vs. values: " + texts.size() + " " + values.size()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final var gString = new GStringExpression(verbatimText, texts, values);
|
|
||||||
this.positionSetter.setPosition(gString, gStringBodyTextNode);
|
|
||||||
return gString;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -3,7 +3,6 @@ package groowt.view.component.web.transpile;
|
|||||||
import groowt.view.component.web.WebViewComponentBugError;
|
import groowt.view.component.web.WebViewComponentBugError;
|
||||||
import groowt.view.component.web.ast.node.*;
|
import groowt.view.component.web.ast.node.*;
|
||||||
import groowt.view.component.web.transpile.groovy.GroovyUtil;
|
import groowt.view.component.web.transpile.groovy.GroovyUtil;
|
||||||
import jakarta.inject.Inject;
|
|
||||||
import org.codehaus.groovy.ast.expr.*;
|
import org.codehaus.groovy.ast.expr.*;
|
||||||
import org.codehaus.groovy.ast.stmt.BlockStatement;
|
import org.codehaus.groovy.ast.stmt.BlockStatement;
|
||||||
import org.codehaus.groovy.ast.stmt.ExpressionStatement;
|
import org.codehaus.groovy.ast.stmt.ExpressionStatement;
|
||||||
@ -16,7 +15,6 @@ public class DefaultGroovyBodyNodeTranspiler implements GroovyBodyNodeTranspiler
|
|||||||
private final PositionSetter positionSetter;
|
private final PositionSetter positionSetter;
|
||||||
private final LeftShiftFactory leftShiftFactory;
|
private final LeftShiftFactory leftShiftFactory;
|
||||||
|
|
||||||
@Inject
|
|
||||||
public DefaultGroovyBodyNodeTranspiler(PositionSetter positionSetter, LeftShiftFactory leftShiftFactory) {
|
public DefaultGroovyBodyNodeTranspiler(PositionSetter positionSetter, LeftShiftFactory leftShiftFactory) {
|
||||||
this.positionSetter = positionSetter;
|
this.positionSetter = positionSetter;
|
||||||
this.leftShiftFactory = leftShiftFactory;
|
this.leftShiftFactory = leftShiftFactory;
|
||||||
@ -58,7 +56,7 @@ public class DefaultGroovyBodyNodeTranspiler implements GroovyBodyNodeTranspiler
|
|||||||
);
|
);
|
||||||
callExpr = new MethodCallExpression(cl, "call", argsList);
|
callExpr = new MethodCallExpression(cl, "call", argsList);
|
||||||
}
|
}
|
||||||
return this.leftShiftFactory.create(callExpr);
|
return this.leftShiftFactory.create(state, callExpr);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Statement handlePlainScriptlet(PlainScriptletNode plainScriptletNode, TranspilerState state) {
|
protected Statement handlePlainScriptlet(PlainScriptletNode plainScriptletNode, TranspilerState state) {
|
||||||
@ -76,7 +74,7 @@ public class DefaultGroovyBodyNodeTranspiler implements GroovyBodyNodeTranspiler
|
|||||||
return new ExpressionStatement(callExpr);
|
return new ExpressionStatement(callExpr);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Statement handleDollarScriptlet(DollarScriptletNode dollarScriptletNode) {
|
protected Statement handleDollarScriptlet(DollarScriptletNode dollarScriptletNode, TranspilerState state) {
|
||||||
final ClosureExpression cl = this.convertToClosure(dollarScriptletNode, dollarScriptletNode.getGroovyCode());
|
final ClosureExpression cl = this.convertToClosure(dollarScriptletNode, dollarScriptletNode.getGroovyCode());
|
||||||
final Expression toLeftShift;
|
final Expression toLeftShift;
|
||||||
if (cl.getParameters() == null) {
|
if (cl.getParameters() == null) {
|
||||||
@ -89,10 +87,10 @@ public class DefaultGroovyBodyNodeTranspiler implements GroovyBodyNodeTranspiler
|
|||||||
toLeftShift = cl;
|
toLeftShift = cl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this.leftShiftFactory.create(toLeftShift);
|
return this.leftShiftFactory.create(state, toLeftShift);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Statement handleDollarReference(DollarReferenceNode dollarReferenceNode) {
|
protected Statement handleDollarReference(DollarReferenceNode dollarReferenceNode, TranspilerState state) {
|
||||||
VariableExpression root = null;
|
VariableExpression root = null;
|
||||||
PropertyExpression propertyExpr = null;
|
PropertyExpression propertyExpr = null;
|
||||||
for (final String part : dollarReferenceNode.getParts()) {
|
for (final String part : dollarReferenceNode.getParts()) {
|
||||||
@ -107,10 +105,10 @@ public class DefaultGroovyBodyNodeTranspiler implements GroovyBodyNodeTranspiler
|
|||||||
final var positionVisitor = new PositionVisitor(this.positionSetter, dollarReferenceNode);
|
final var positionVisitor = new PositionVisitor(this.positionSetter, dollarReferenceNode);
|
||||||
if (propertyExpr != null) {
|
if (propertyExpr != null) {
|
||||||
propertyExpr.visit(positionVisitor);
|
propertyExpr.visit(positionVisitor);
|
||||||
return this.leftShiftFactory.create(propertyExpr);
|
return this.leftShiftFactory.create(state, propertyExpr);
|
||||||
} else if (root != null) {
|
} else if (root != null) {
|
||||||
root.visit(positionVisitor);
|
root.visit(positionVisitor);
|
||||||
return this.leftShiftFactory.create(root);
|
return this.leftShiftFactory.create(state, root);
|
||||||
} else {
|
} else {
|
||||||
throw new WebViewComponentBugError("Did not expect root to be null.");
|
throw new WebViewComponentBugError("Did not expect root to be null.");
|
||||||
}
|
}
|
||||||
@ -121,8 +119,8 @@ public class DefaultGroovyBodyNodeTranspiler implements GroovyBodyNodeTranspiler
|
|||||||
return switch (groovyBodyNode) {
|
return switch (groovyBodyNode) {
|
||||||
case EqualsScriptletNode equalsScriptletNode -> this.handleEqualsScriptlet(equalsScriptletNode, state);
|
case EqualsScriptletNode equalsScriptletNode -> this.handleEqualsScriptlet(equalsScriptletNode, state);
|
||||||
case PlainScriptletNode plainScriptletNode -> this.handlePlainScriptlet(plainScriptletNode, state);
|
case PlainScriptletNode plainScriptletNode -> this.handlePlainScriptlet(plainScriptletNode, state);
|
||||||
case DollarScriptletNode dollarScriptletNode -> this.handleDollarScriptlet(dollarScriptletNode);
|
case DollarScriptletNode dollarScriptletNode -> this.handleDollarScriptlet(dollarScriptletNode, state);
|
||||||
case DollarReferenceNode dollarReferenceNode -> this.handleDollarReference(dollarReferenceNode);
|
case DollarReferenceNode dollarReferenceNode -> this.handleDollarReference(dollarReferenceNode, state);
|
||||||
default -> throw new WebViewComponentBugError(new UnsupportedOperationException(
|
default -> throw new WebViewComponentBugError(new UnsupportedOperationException(
|
||||||
"GroovyBodyNode of type " + groovyBodyNode.getClass().getName() + " is not supported."
|
"GroovyBodyNode of type " + groovyBodyNode.getClass().getName() + " is not supported."
|
||||||
));
|
));
|
||||||
|
@ -4,14 +4,12 @@ import groovy.transform.Field;
|
|||||||
import groowt.view.component.compiler.ComponentTemplateCompileException;
|
import groowt.view.component.compiler.ComponentTemplateCompileException;
|
||||||
import groowt.view.component.compiler.ComponentTemplateCompileUnit;
|
import groowt.view.component.compiler.ComponentTemplateCompileUnit;
|
||||||
import groowt.view.component.compiler.ComponentTemplateCompilerConfiguration;
|
import groowt.view.component.compiler.ComponentTemplateCompilerConfiguration;
|
||||||
import groowt.view.component.web.WebViewComponentBugError;
|
|
||||||
import groowt.view.component.web.ast.node.BodyNode;
|
import groowt.view.component.web.ast.node.BodyNode;
|
||||||
import groowt.view.component.web.ast.node.CompilationUnitNode;
|
import groowt.view.component.web.ast.node.CompilationUnitNode;
|
||||||
import groowt.view.component.web.ast.node.PreambleNode;
|
import groowt.view.component.web.ast.node.PreambleNode;
|
||||||
import groowt.view.component.web.compiler.MultipleWebViewComponentCompileErrorsException;
|
import groowt.view.component.web.compiler.MultipleWebViewComponentCompileErrorsException;
|
||||||
import groowt.view.component.web.compiler.WebViewComponentTemplateCompileException;
|
import groowt.view.component.web.compiler.WebViewComponentTemplateCompileException;
|
||||||
import groowt.view.component.web.compiler.WebViewComponentTemplateCompileUnit;
|
import groowt.view.component.web.compiler.WebViewComponentTemplateCompileUnit;
|
||||||
import groowt.view.component.web.transpile.BodyTranspiler.AddOrAppendCallback;
|
|
||||||
import groowt.view.component.web.transpile.groovy.GroovyUtil;
|
import groowt.view.component.web.transpile.groovy.GroovyUtil;
|
||||||
import groowt.view.component.web.transpile.resolve.ClassLoaderComponentClassNodeResolver;
|
import groowt.view.component.web.transpile.resolve.ClassLoaderComponentClassNodeResolver;
|
||||||
import org.codehaus.groovy.ast.*;
|
import org.codehaus.groovy.ast.*;
|
||||||
@ -37,10 +35,9 @@ public class DefaultGroovyTranspiler implements GroovyTranspiler {
|
|||||||
private static final ClassNode FIELD_ANNOTATION = ClassHelper.make(Field.class);
|
private static final ClassNode FIELD_ANNOTATION = ClassHelper.make(Field.class);
|
||||||
|
|
||||||
protected TranspilerConfiguration getConfiguration(
|
protected TranspilerConfiguration getConfiguration(
|
||||||
WebViewComponentTemplateCompileUnit compileUnit,
|
ClassLoaderComponentClassNodeResolver classLoaderComponentClassNodeResolver
|
||||||
ClassLoader classLoader
|
|
||||||
) {
|
) {
|
||||||
return new DefaultTranspilerConfiguration(new ClassLoaderComponentClassNodeResolver(compileUnit, classLoader));
|
return SimpleTranspilerConfiguration.withDefaults(classLoaderComponentClassNodeResolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void addAutomaticImports(WebViewComponentModuleNode moduleNode, TranspilerConfiguration configuration) {
|
protected void addAutomaticImports(WebViewComponentModuleNode moduleNode, TranspilerConfiguration configuration) {
|
||||||
@ -235,20 +232,7 @@ public class DefaultGroovyTranspiler implements GroovyTranspiler {
|
|||||||
TranspilerConfiguration transpilerConfiguration,
|
TranspilerConfiguration transpilerConfiguration,
|
||||||
TranspilerState state
|
TranspilerState state
|
||||||
) {
|
) {
|
||||||
final var appendOrAddStatementFactory = transpilerConfiguration.getAppendOrAddStatementFactory();
|
return transpilerConfiguration.getBodyTranspiler().transpileBody(bodyNode, state);
|
||||||
final AddOrAppendCallback callback = (source, expr) -> appendOrAddStatementFactory.addOrAppend(
|
|
||||||
source,
|
|
||||||
state,
|
|
||||||
action -> {
|
|
||||||
if (action == AppendOrAddStatementFactory.Action.ADD) {
|
|
||||||
throw new WebViewComponentBugError(new IllegalStateException(
|
|
||||||
"Should not be adding from document root, only appending!"
|
|
||||||
));
|
|
||||||
}
|
|
||||||
return expr;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return transpilerConfiguration.getBodyTranspiler().transpileBody(bodyNode, callback, state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -258,10 +242,12 @@ public class DefaultGroovyTranspiler implements GroovyTranspiler {
|
|||||||
CompilationUnitNode compilationUnitNode,
|
CompilationUnitNode compilationUnitNode,
|
||||||
String templateClassSimpleName
|
String templateClassSimpleName
|
||||||
) throws ComponentTemplateCompileException {
|
) throws ComponentTemplateCompileException {
|
||||||
// transpilerConfiguration and positionSetter
|
// resolver, transpilerConfiguration, and positionSetter
|
||||||
final var transpilerConfiguration = this.getConfiguration(
|
final ClassLoaderComponentClassNodeResolver resolver = new ClassLoaderComponentClassNodeResolver(
|
||||||
compileUnit, compileUnit.getGroovyCompilationUnit().getClassLoader()
|
compileUnit,
|
||||||
|
compileUnit.getGroovyCompilationUnit().getClassLoader()
|
||||||
);
|
);
|
||||||
|
final var transpilerConfiguration = this.getConfiguration(resolver);
|
||||||
final PositionSetter positionSetter = transpilerConfiguration.getPositionSetter();
|
final PositionSetter positionSetter = transpilerConfiguration.getPositionSetter();
|
||||||
|
|
||||||
// prepare sourceUnit
|
// prepare sourceUnit
|
||||||
@ -280,6 +266,9 @@ public class DefaultGroovyTranspiler implements GroovyTranspiler {
|
|||||||
compileUnit, sourceUnit, transpilerConfiguration
|
compileUnit, sourceUnit, transpilerConfiguration
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// set resolver's moduleNode
|
||||||
|
resolver.setModuleNode(moduleNode);
|
||||||
|
|
||||||
// prepare mainClassNode
|
// prepare mainClassNode
|
||||||
final ClassNode mainClassNode = this.initMainClassNode(compileUnit, templateClassSimpleName, moduleNode);
|
final ClassNode mainClassNode = this.initMainClassNode(compileUnit, templateClassSimpleName, moduleNode);
|
||||||
|
|
||||||
|
@ -1,47 +0,0 @@
|
|||||||
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 org.codehaus.groovy.ast.expr.ConstantExpression;
|
|
||||||
|
|
||||||
import static org.apache.groovy.parser.antlr4.util.StringUtils.*;
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public class DefaultJStringTranspiler implements JStringTranspiler {
|
|
||||||
|
|
||||||
private final PositionSetter positionSetter;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public DefaultJStringTranspiler(PositionSetter positionSetter) {
|
|
||||||
this.positionSetter = positionSetter;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ConstantExpression createStringLiteral(JStringBodyTextNode jStringBodyTextNode) {
|
|
||||||
final var withoutCR = removeCR(jStringBodyTextNode.getContent());
|
|
||||||
final var escaped = replaceEscapes(withoutCR, NONE_SLASHY);
|
|
||||||
final var expression = new ConstantExpression(escaped);
|
|
||||||
expression.setNodeMetaData("_IS_STRING", true);
|
|
||||||
this.positionSetter.setPosition(expression, jStringBodyTextNode);
|
|
||||||
return expression;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ConstantExpression createStringLiteral(JStringValueNode jStringValueNode) {
|
|
||||||
final var content = jStringValueNode.getContent();
|
|
||||||
final var escaped = replaceEscapes(content, NONE_SLASHY);
|
|
||||||
final var expression = new ConstantExpression(escaped);
|
|
||||||
expression.setNodeMetaData("_IS_STRING", true);
|
|
||||||
this.positionSetter.setPosition(expression, jStringValueNode);
|
|
||||||
return expression;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ConstantExpression createEmptyStringLiteral() {
|
|
||||||
final var expression = new ConstantExpression("");
|
|
||||||
expression.setNodeMetaData("_IS_STRING", true);
|
|
||||||
return expression;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,28 @@
|
|||||||
|
package groowt.view.component.web.transpile;
|
||||||
|
|
||||||
|
import org.codehaus.groovy.ast.expr.BinaryExpression;
|
||||||
|
import org.codehaus.groovy.ast.expr.Expression;
|
||||||
|
import org.codehaus.groovy.ast.stmt.ExpressionStatement;
|
||||||
|
import org.codehaus.groovy.ast.stmt.Statement;
|
||||||
|
import org.codehaus.groovy.syntax.Token;
|
||||||
|
import org.codehaus.groovy.syntax.Types;
|
||||||
|
|
||||||
|
public class DefaultLeftShiftFactory implements LeftShiftFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Statement create(TranspilerState state, Expression rightSide) {
|
||||||
|
final Expression left;
|
||||||
|
if (state.hasCurrentChildList()) {
|
||||||
|
left = state.getCurrentChildList();
|
||||||
|
} else {
|
||||||
|
left = state.getWriter();
|
||||||
|
}
|
||||||
|
final BinaryExpression leftShift = new BinaryExpression(
|
||||||
|
left,
|
||||||
|
new Token(Types.LEFT_SHIFT, "<<", -1, -1),
|
||||||
|
rightSide
|
||||||
|
);
|
||||||
|
return new ExpressionStatement(leftShift);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,85 +0,0 @@
|
|||||||
package groowt.view.component.web.transpile;
|
|
||||||
|
|
||||||
import groovy.lang.Tuple3;
|
|
||||||
import groowt.util.fp.provider.DefaultProvider;
|
|
||||||
import groowt.view.component.web.transpile.resolve.ComponentClassNodeResolver;
|
|
||||||
import org.codehaus.groovy.ast.ClassNode;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import static groowt.view.component.web.transpile.TranspilerUtil.*;
|
|
||||||
|
|
||||||
public class DefaultTranspilerConfiguration implements TranspilerConfiguration {
|
|
||||||
|
|
||||||
private final PositionSetter positionSetter;
|
|
||||||
private final AppendOrAddStatementFactory appendOrAddStatementFactory = new DefaultAppendOrAddStatementFactory();
|
|
||||||
private final BodyTranspiler bodyTranspiler;
|
|
||||||
private final ValueNodeTranspiler valueNodeTranspiler;
|
|
||||||
|
|
||||||
public DefaultTranspilerConfiguration(ComponentClassNodeResolver classNodeResolver) {
|
|
||||||
this.positionSetter = new SimplePositionSetter();
|
|
||||||
final var jStringTranspiler = new DefaultJStringTranspiler(this.positionSetter);
|
|
||||||
final var gStringTranspiler = new DefaultGStringTranspiler(this.positionSetter, jStringTranspiler);
|
|
||||||
final var componentTranspiler = new DefaultComponentTranspiler(
|
|
||||||
DefaultProvider.of(this.appendOrAddStatementFactory),
|
|
||||||
DefaultProvider.of(classNodeResolver),
|
|
||||||
DefaultProvider.ofLazy(ValueNodeTranspiler.class, this::getValueNodeTranspiler),
|
|
||||||
DefaultProvider.ofLazy(BodyTranspiler.class, this::getBodyTranspiler)
|
|
||||||
);
|
|
||||||
this.valueNodeTranspiler = new DefaultValueNodeTranspiler(componentTranspiler);
|
|
||||||
this.bodyTranspiler = new DefaultBodyTranspiler(componentTranspiler, null); // TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PositionSetter getPositionSetter() {
|
|
||||||
return this.positionSetter;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BodyTranspiler getBodyTranspiler() {
|
|
||||||
return this.bodyTranspiler;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AppendOrAddStatementFactory getAppendOrAddStatementFactory() {
|
|
||||||
return this.appendOrAddStatementFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ValueNodeTranspiler getValueNodeTranspiler() {
|
|
||||||
return this.valueNodeTranspiler;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, ClassNode> getImports() {
|
|
||||||
return Map.of(
|
|
||||||
COMPONENT_TEMPLATE.getNameWithoutPackage(), COMPONENT_TEMPLATE,
|
|
||||||
COMPONENT_CONTEXT_TYPE.getNameWithoutPackage(), COMPONENT_CONTEXT_TYPE
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<String> getStarImports() {
|
|
||||||
return Set.of(
|
|
||||||
GROOWT_VIEW_COMPONENT_WEB + ".lib",
|
|
||||||
"groowt.view.component.runtime",
|
|
||||||
GROOWT_VIEW_COMPONENT_WEB + ".runtime"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<Tuple3<ClassNode, String, String>> getStaticImports() {
|
|
||||||
return Set.of();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, ClassNode> getStaticStarImports() {
|
|
||||||
return Map.of();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ClassNode getRenderContextImplementation() {
|
|
||||||
return DEFAULT_RENDER_CONTEXT_IMPLEMENTATION;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,6 +1,5 @@
|
|||||||
package groowt.view.component.web.transpile;
|
package groowt.view.component.web.transpile;
|
||||||
|
|
||||||
import groowt.view.component.web.WebViewComponentBugError;
|
|
||||||
import groowt.view.component.web.ast.node.*;
|
import groowt.view.component.web.ast.node.*;
|
||||||
import groowt.view.component.web.transpile.groovy.GroovyUtil;
|
import groowt.view.component.web.transpile.groovy.GroovyUtil;
|
||||||
import groowt.view.component.web.transpile.groovy.GroovyUtil.ConvertResult;
|
import groowt.view.component.web.transpile.groovy.GroovyUtil.ConvertResult;
|
||||||
@ -12,33 +11,30 @@ import org.codehaus.groovy.ast.stmt.ExpressionStatement;
|
|||||||
import org.codehaus.groovy.ast.stmt.Statement;
|
import org.codehaus.groovy.ast.stmt.Statement;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static groowt.view.component.web.transpile.TranspilerUtil.getStringLiteral;
|
import static groowt.view.component.web.transpile.TranspilerUtil.getStringLiteral;
|
||||||
|
|
||||||
// TODO: set positions
|
|
||||||
public class DefaultValueNodeTranspiler implements ValueNodeTranspiler {
|
public class DefaultValueNodeTranspiler implements ValueNodeTranspiler {
|
||||||
|
|
||||||
private final ComponentTranspiler componentTranspiler;
|
private final ComponentTranspiler componentTranspiler;
|
||||||
|
private final PositionSetter positionSetter;
|
||||||
|
|
||||||
public DefaultValueNodeTranspiler(ComponentTranspiler componentTranspiler) {
|
public DefaultValueNodeTranspiler(ComponentTranspiler componentTranspiler, PositionSetter positionSetter) {
|
||||||
this.componentTranspiler = componentTranspiler;
|
this.componentTranspiler = componentTranspiler;
|
||||||
|
this.positionSetter = positionSetter;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: positions
|
|
||||||
protected Expression handleClosureNode(ClosureValueNode closureValueNode) {
|
protected Expression handleClosureNode(ClosureValueNode closureValueNode) {
|
||||||
final var rawCode = closureValueNode.getGroovyCode().getAsValidGroovyCode();
|
final var rawCode = closureValueNode.getGroovyCode().getAsValidGroovyCode();
|
||||||
final ClosureExpression convertedClosure = GroovyUtil.getClosure(rawCode);
|
final ClosureExpression convertedClosure = GroovyUtil.getClosure(rawCode);
|
||||||
|
|
||||||
|
final PositionVisitor positionVisitor = new PositionVisitor(
|
||||||
|
this.positionSetter.withOffset(0, -10),
|
||||||
|
closureValueNode
|
||||||
|
);
|
||||||
|
convertedClosure.visit(positionVisitor);
|
||||||
|
|
||||||
final Statement closureCode = convertedClosure.getCode();
|
final Statement closureCode = convertedClosure.getCode();
|
||||||
if (closureCode instanceof BlockStatement blockStatement) {
|
if (closureCode instanceof ExpressionStatement expressionStatement) {
|
||||||
final List<Statement> statements = blockStatement.getStatements();
|
|
||||||
if (statements.isEmpty()) {
|
|
||||||
throw new WebViewComponentBugError(new IllegalArgumentException(
|
|
||||||
"Did not expect ClosureValueNode to produce no statements."
|
|
||||||
));
|
|
||||||
} else if (statements.size() == 1) {
|
|
||||||
final Statement statement = statements.getFirst();
|
|
||||||
if (statement instanceof ExpressionStatement expressionStatement) {
|
|
||||||
final Expression expression = expressionStatement.getExpression();
|
final Expression expression = expressionStatement.getExpression();
|
||||||
return switch (expression) {
|
return switch (expression) {
|
||||||
case ConstantExpression ignored -> expression;
|
case ConstantExpression ignored -> expression;
|
||||||
@ -46,16 +42,9 @@ public class DefaultValueNodeTranspiler implements ValueNodeTranspiler {
|
|||||||
case PropertyExpression ignored -> expression;
|
case PropertyExpression ignored -> expression;
|
||||||
default -> convertedClosure;
|
default -> convertedClosure;
|
||||||
};
|
};
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException("A component closure value must produce a value.");
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return convertedClosure;
|
return convertedClosure;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return convertedClosure;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Expression gStringValue(GStringValueNode gStringValueNode) {
|
private Expression gStringValue(GStringValueNode gStringValueNode) {
|
||||||
final var rawCode = gStringValueNode.getGroovyCode().getAsValidGroovyCode();
|
final var rawCode = gStringValueNode.getGroovyCode().getAsValidGroovyCode();
|
||||||
@ -65,16 +54,27 @@ public class DefaultValueNodeTranspiler implements ValueNodeTranspiler {
|
|||||||
throw new IllegalStateException("block statement is null or empty");
|
throw new IllegalStateException("block statement is null or empty");
|
||||||
}
|
}
|
||||||
final ExpressionStatement exprStmt = (ExpressionStatement) blockStatement.getStatements().getFirst();
|
final ExpressionStatement exprStmt = (ExpressionStatement) blockStatement.getStatements().getFirst();
|
||||||
// TODO: set pos
|
|
||||||
|
final PositionVisitor positionVisitor = new PositionVisitor(
|
||||||
|
this.positionSetter.withOffset(0, -1),
|
||||||
|
gStringValueNode
|
||||||
|
);
|
||||||
|
exprStmt.visit(positionVisitor);
|
||||||
|
|
||||||
return exprStmt.getExpression();
|
return exprStmt.getExpression();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConstantExpression jStringValue(JStringValueNode jStringValueNode) {
|
private ConstantExpression jStringValue(JStringValueNode jStringValueNode) {
|
||||||
return getStringLiteral(jStringValueNode.getContent()); // TODO: set pos
|
final ConstantExpression literal = getStringLiteral(jStringValueNode.getContent());
|
||||||
|
this.positionSetter.setPosition(literal, jStringValueNode);
|
||||||
|
return literal;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClosureExpression emptyClosureValue(EmptyClosureValueNode emptyClosureValueNode) {
|
private ClosureExpression emptyClosureValue(EmptyClosureValueNode emptyClosureValueNode) {
|
||||||
return new ClosureExpression(Parameter.EMPTY_ARRAY, EmptyStatement.INSTANCE); // TODO: set pos
|
final ClosureExpression cl = new ClosureExpression(Parameter.EMPTY_ARRAY, EmptyStatement.INSTANCE);
|
||||||
|
final PositionVisitor positionVisitor = new PositionVisitor(this.positionSetter, emptyClosureValueNode);
|
||||||
|
cl.visit(positionVisitor);
|
||||||
|
return cl;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClosureExpression componentValue(ComponentValueNode componentValueNode, TranspilerState state) {
|
private ClosureExpression componentValue(ComponentValueNode componentValueNode, TranspilerState state) {
|
||||||
@ -84,7 +84,7 @@ public class DefaultValueNodeTranspiler implements ValueNodeTranspiler {
|
|||||||
componentValueNode.getComponentNode(),
|
componentValueNode.getComponentNode(),
|
||||||
state
|
state
|
||||||
), state.getCurrentScope())
|
), state.getCurrentScope())
|
||||||
); // TODO: set pos
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
package groowt.view.component.web.transpile;
|
|
||||||
|
|
||||||
import groowt.view.component.web.ast.node.GStringBodyTextNode;
|
|
||||||
import org.codehaus.groovy.ast.expr.GStringExpression;
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public interface GStringTranspiler {
|
|
||||||
GStringExpression createGStringExpression(GStringBodyTextNode parent);
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
package groowt.view.component.web.transpile;
|
|
||||||
|
|
||||||
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);
|
|
||||||
ConstantExpression createEmptyStringLiteral();
|
|
||||||
}
|
|
@ -4,5 +4,5 @@ import org.codehaus.groovy.ast.expr.Expression;
|
|||||||
import org.codehaus.groovy.ast.stmt.Statement;
|
import org.codehaus.groovy.ast.stmt.Statement;
|
||||||
|
|
||||||
public interface LeftShiftFactory {
|
public interface LeftShiftFactory {
|
||||||
Statement create(Expression rightSide);
|
Statement create(TranspilerState state, Expression rightSide);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,150 @@
|
|||||||
|
package groowt.view.component.web.transpile;
|
||||||
|
|
||||||
|
import groovy.lang.Tuple3;
|
||||||
|
import groowt.view.component.web.transpile.resolve.ComponentClassNodeResolver;
|
||||||
|
import org.codehaus.groovy.ast.ClassNode;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static groowt.view.component.web.transpile.TranspilerUtil.*;
|
||||||
|
|
||||||
|
public class SimpleTranspilerConfiguration implements TranspilerConfiguration {
|
||||||
|
|
||||||
|
public static TranspilerConfiguration withDefaults(ComponentClassNodeResolver componentClassNodeResolver) {
|
||||||
|
final var c = new SimpleTranspilerConfiguration();
|
||||||
|
c.setComponentClassNodeResolver(componentClassNodeResolver);
|
||||||
|
|
||||||
|
final var ct = new DefaultComponentTranspiler();
|
||||||
|
final PositionSetter ps = new SimplePositionSetter();
|
||||||
|
final LeftShiftFactory lsf = new DefaultLeftShiftFactory();
|
||||||
|
final var gbnt = new DefaultGroovyBodyNodeTranspiler(ps, lsf);
|
||||||
|
final var btt = new DefaultBodyTextTranspiler(gbnt, ps, lsf, true);
|
||||||
|
final var bt = new DefaultBodyTranspiler(ct, btt);
|
||||||
|
final var vnt = new DefaultValueNodeTranspiler(ct, ps);
|
||||||
|
|
||||||
|
ct.setLeftShiftFactory(lsf);
|
||||||
|
ct.setBodyTranspiler(bt);
|
||||||
|
ct.setValueNodeTranspiler(vnt);
|
||||||
|
ct.setComponentClassNodeResolver(componentClassNodeResolver);
|
||||||
|
|
||||||
|
c.setComponentTranspiler(ct);
|
||||||
|
c.setPositionSetter(ps);
|
||||||
|
c.setLeftShiftFactory(lsf);
|
||||||
|
c.setGroovyBodyNodeTranspiler(gbnt);
|
||||||
|
c.setBodyTextTranspiler(btt);
|
||||||
|
c.setBodyTranspiler(bt);
|
||||||
|
c.setValueNodeTranspiler(vnt);
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ComponentClassNodeResolver componentClassNodeResolver;
|
||||||
|
private ComponentTranspiler componentTranspiler;
|
||||||
|
private PositionSetter positionSetter;
|
||||||
|
private LeftShiftFactory leftShiftFactory;
|
||||||
|
private GroovyBodyNodeTranspiler groovyBodyNodeTranspiler;
|
||||||
|
private BodyTextTranspiler bodyTextTranspiler;
|
||||||
|
private BodyTranspiler bodyTranspiler;
|
||||||
|
private ValueNodeTranspiler valueNodeTranspiler;
|
||||||
|
|
||||||
|
public ComponentClassNodeResolver getComponentClassNodeResolver() {
|
||||||
|
return Objects.requireNonNull(this.componentClassNodeResolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setComponentClassNodeResolver(ComponentClassNodeResolver componentClassNodeResolver) {
|
||||||
|
this.componentClassNodeResolver = componentClassNodeResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ComponentTranspiler getComponentTranspiler() {
|
||||||
|
return Objects.requireNonNull(this.componentTranspiler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setComponentTranspiler(ComponentTranspiler componentTranspiler) {
|
||||||
|
this.componentTranspiler = componentTranspiler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PositionSetter getPositionSetter() {
|
||||||
|
return Objects.requireNonNull(this.positionSetter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPositionSetter(PositionSetter positionSetter) {
|
||||||
|
this.positionSetter = positionSetter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LeftShiftFactory getLeftShiftFactory() {
|
||||||
|
return this.leftShiftFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLeftShiftFactory(LeftShiftFactory leftShiftFactory) {
|
||||||
|
this.leftShiftFactory = leftShiftFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GroovyBodyNodeTranspiler getGroovyBodyNodeTranspiler() {
|
||||||
|
return this.groovyBodyNodeTranspiler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGroovyBodyNodeTranspiler(GroovyBodyNodeTranspiler groovyBodyNodeTranspiler) {
|
||||||
|
this.groovyBodyNodeTranspiler = groovyBodyNodeTranspiler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BodyTextTranspiler getBodyTextTranspiler() {
|
||||||
|
return this.bodyTextTranspiler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBodyTextTranspiler(BodyTextTranspiler bodyTextTranspiler) {
|
||||||
|
this.bodyTextTranspiler = bodyTextTranspiler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BodyTranspiler getBodyTranspiler() {
|
||||||
|
return Objects.requireNonNull(this.bodyTranspiler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBodyTranspiler(BodyTranspiler bodyTranspiler) {
|
||||||
|
this.bodyTranspiler = bodyTranspiler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueNodeTranspiler getValueNodeTranspiler() {
|
||||||
|
return Objects.requireNonNull(this.valueNodeTranspiler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValueNodeTranspiler(ValueNodeTranspiler valueNodeTranspiler) {
|
||||||
|
this.valueNodeTranspiler = valueNodeTranspiler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, ClassNode> getImports() {
|
||||||
|
return Map.of(
|
||||||
|
COMPONENT_TEMPLATE.getNameWithoutPackage(), COMPONENT_TEMPLATE,
|
||||||
|
COMPONENT_CONTEXT_TYPE.getNameWithoutPackage(), COMPONENT_CONTEXT_TYPE
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> getStarImports() {
|
||||||
|
return Set.of(
|
||||||
|
GROOWT_VIEW_COMPONENT_WEB + ".lib",
|
||||||
|
"groowt.view.component.runtime",
|
||||||
|
GROOWT_VIEW_COMPONENT_WEB + ".runtime"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Tuple3<ClassNode, String, String>> getStaticImports() {
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, ClassNode> getStaticStarImports() {
|
||||||
|
return Map.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClassNode getRenderContextImplementation() {
|
||||||
|
return DEFAULT_RENDER_CONTEXT_IMPLEMENTATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -9,7 +9,6 @@ import java.util.Set;
|
|||||||
public interface TranspilerConfiguration {
|
public interface TranspilerConfiguration {
|
||||||
PositionSetter getPositionSetter();
|
PositionSetter getPositionSetter();
|
||||||
BodyTranspiler getBodyTranspiler();
|
BodyTranspiler getBodyTranspiler();
|
||||||
AppendOrAddStatementFactory getAppendOrAddStatementFactory();
|
|
||||||
Map<String, ClassNode> getImports();
|
Map<String, ClassNode> getImports();
|
||||||
Set<String> getStarImports();
|
Set<String> getStarImports();
|
||||||
Set<Tuple3<ClassNode, String, String>> getStaticImports();
|
Set<Tuple3<ClassNode, String, String>> getStaticImports();
|
||||||
|
@ -6,13 +6,12 @@ import groowt.view.component.web.antlr.TokenList
|
|||||||
import groowt.view.component.web.antlr.WebViewComponentsLexer
|
import groowt.view.component.web.antlr.WebViewComponentsLexer
|
||||||
import groowt.view.component.web.antlr.WebViewComponentsParser
|
import groowt.view.component.web.antlr.WebViewComponentsParser
|
||||||
import groowt.view.component.web.antlr.WebViewComponentsTokenStream
|
import groowt.view.component.web.antlr.WebViewComponentsTokenStream
|
||||||
import groowt.view.component.web.ast.node.*
|
import groowt.view.component.web.ast.node.Node
|
||||||
import org.antlr.v4.runtime.CharStreams
|
import org.antlr.v4.runtime.CharStreams
|
||||||
import org.junit.jupiter.api.Disabled
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
|
|
||||||
import static groowt.view.component.web.antlr.WebViewComponentsParser.CompilationUnitContext
|
import static groowt.view.component.web.antlr.WebViewComponentsParser.CompilationUnitContext
|
||||||
import static org.junit.jupiter.api.Assertions.*
|
import static org.junit.jupiter.api.Assertions.assertInstanceOf
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull
|
||||||
|
|
||||||
class DefaultAstBuilderVisitorTests {
|
class DefaultAstBuilderVisitorTests {
|
||||||
|
|
||||||
@ -52,30 +51,4 @@ class DefaultAstBuilderVisitorTests {
|
|||||||
return new Tuple2<>(cu.accept(visitor), tokenList)
|
return new Tuple2<>(cu.accept(visitor), tokenList)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
@Disabled('Move to file tests.')
|
|
||||||
void helloTarget() {
|
|
||||||
def (node, tokenList) = this.doBuild('Hello, $target!')
|
|
||||||
assertNodeWith(CompilationUnitNode, node) {
|
|
||||||
assertNull(preambleNode)
|
|
||||||
bodyNode.with {
|
|
||||||
assertEquals(1, childrenSize)
|
|
||||||
assertNodeWith(GStringBodyTextNode, children.first) {
|
|
||||||
assertEquals(3, childrenSize)
|
|
||||||
assertNodeWith(JStringBodyTextNode, it[0]) {
|
|
||||||
assertEquals('Hello, ', it.getText(tokenList))
|
|
||||||
assertEquals('Hello, ', it.content)
|
|
||||||
}
|
|
||||||
assertNodeWith(DollarReferenceNode, it[1]) {
|
|
||||||
assertEquals('$target', it.getText(tokenList))
|
|
||||||
assertEquals('$target', it.GStringPath.asValidEmbeddableCode)
|
|
||||||
}
|
|
||||||
assertNodeWith(JStringBodyTextNode, it[2]) {
|
|
||||||
assertEquals('!', it.content)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package groowt.view.component.web.transpiler;
|
package groowt.view.component.web.transpiler;
|
||||||
|
|
||||||
import groowt.view.component.web.compiler.WebViewComponentTemplateCompileUnit;
|
import groowt.view.component.web.compiler.WebViewComponentTemplateCompileUnit;
|
||||||
import groowt.view.component.web.transpile.DefaultTranspilerConfiguration;
|
import groowt.view.component.web.transpile.SimpleTranspilerConfiguration;
|
||||||
import groowt.view.component.web.transpile.TranspilerConfiguration;
|
import groowt.view.component.web.transpile.TranspilerConfiguration;
|
||||||
import groowt.view.component.web.transpile.resolve.CachingComponentClassNodeResolver;
|
import groowt.view.component.web.transpile.resolve.CachingComponentClassNodeResolver;
|
||||||
import org.codehaus.groovy.ast.ModuleNode;
|
import org.codehaus.groovy.ast.ModuleNode;
|
||||||
@ -13,9 +13,7 @@ public class DefaultBodyTranspilerTests extends BodyTranspilerTests {
|
|||||||
WebViewComponentTemplateCompileUnit compileUnit,
|
WebViewComponentTemplateCompileUnit compileUnit,
|
||||||
ModuleNode moduleNode
|
ModuleNode moduleNode
|
||||||
) {
|
) {
|
||||||
return new DefaultTranspilerConfiguration(
|
return SimpleTranspilerConfiguration.withDefaults(new CachingComponentClassNodeResolver(compileUnit));
|
||||||
new CachingComponentClassNodeResolver(compileUnit)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
package groowt.view.component.web.transpiler;
|
|
||||||
|
|
||||||
import groowt.view.component.web.transpile.DefaultGStringTranspiler;
|
|
||||||
import groowt.view.component.web.transpile.DefaultJStringTranspiler;
|
|
||||||
import groowt.view.component.web.transpile.GStringTranspiler;
|
|
||||||
import groowt.view.component.web.transpile.SimplePositionSetter;
|
|
||||||
|
|
||||||
public class DefaultGStringTranspilerTests extends GStringTranspilerTests {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected GStringTranspiler getGStringTranspiler() {
|
|
||||||
final var positionSetter = new SimplePositionSetter();
|
|
||||||
return new DefaultGStringTranspiler(positionSetter, new DefaultJStringTranspiler(positionSetter));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -223,7 +223,7 @@ public abstract class NodeFactoryTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void plainScriptletNode() {
|
public void plainScriptletNode() {
|
||||||
assertNotNull(this.nodeFactory.plainScriptletNode(this.getTokenRange(), 0));
|
assertNotNull(this.nodeFactory.plainScriptletNode(this.getTokenRange(), ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -233,7 +233,7 @@ public abstract class NodeFactoryTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void dollarReferenceNode() {
|
public void dollarReferenceNode() {
|
||||||
assertNotNull(this.nodeFactory.dollarReferenceNode(this.getTokenRange(), 0));
|
assertNotNull(this.nodeFactory.dollarReferenceNode(this.getTokenRange(), List.of("test")));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -8,13 +8,7 @@ import groowt.view.component.web.ast.node.BodyNode;
|
|||||||
import groowt.view.component.web.compiler.WebViewComponentTemplateCompileUnit;
|
import groowt.view.component.web.compiler.WebViewComponentTemplateCompileUnit;
|
||||||
import groowt.view.component.web.transpile.BodyTranspiler;
|
import groowt.view.component.web.transpile.BodyTranspiler;
|
||||||
import groowt.view.component.web.transpile.TranspilerConfiguration;
|
import groowt.view.component.web.transpile.TranspilerConfiguration;
|
||||||
import groowt.view.component.web.transpile.TranspilerState;
|
|
||||||
import org.codehaus.groovy.ast.ModuleNode;
|
import org.codehaus.groovy.ast.ModuleNode;
|
||||||
import org.codehaus.groovy.ast.expr.ConstantExpression;
|
|
||||||
import org.codehaus.groovy.ast.expr.MethodCallExpression;
|
|
||||||
import org.codehaus.groovy.ast.expr.TupleExpression;
|
|
||||||
import org.codehaus.groovy.ast.stmt.BlockStatement;
|
|
||||||
import org.codehaus.groovy.ast.stmt.ExpressionStatement;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
@ -30,9 +24,7 @@ public abstract class BodyTranspilerTests {
|
|||||||
ModuleNode moduleNode
|
ModuleNode moduleNode
|
||||||
);
|
);
|
||||||
|
|
||||||
protected record BuildResult(BodyNode bodyNode, TokenList tokenList) {
|
protected record BuildResult(BodyNode bodyNode, TokenList tokenList) {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected BuildResult build(String source) {
|
protected BuildResult build(String source) {
|
||||||
final var parseResult = ParserUtil.parseCompilationUnit(source);
|
final var parseResult = ParserUtil.parseCompilationUnit(source);
|
||||||
@ -58,47 +50,4 @@ public abstract class BodyTranspilerTests {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void simpleGStringOutStatement(
|
|
||||||
@Mock WebViewComponentTemplateCompileUnit compileUnit,
|
|
||||||
@Mock ModuleNode moduleNode
|
|
||||||
) {
|
|
||||||
final var source = "Hello, $target!";
|
|
||||||
final var buildResult = this.build(source);
|
|
||||||
final var configuration = this.getConfiguration(compileUnit, moduleNode);
|
|
||||||
final var transpiler = this.getBodyTranspiler(configuration);
|
|
||||||
final var state = TranspilerState.withDefaultRootScope();
|
|
||||||
final var addOrAppend = configuration.getAppendOrAddStatementFactory();
|
|
||||||
final BlockStatement blockStatement = transpiler.transpileBody(
|
|
||||||
buildResult.bodyNode(),
|
|
||||||
(node, expression) -> addOrAppend.addOrAppend(node, state, ignored -> expression),
|
|
||||||
TranspilerState.withDefaultRootScope()
|
|
||||||
);
|
|
||||||
assertEquals(1, blockStatement.getStatements().size());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void simpleJStringOutStatement(
|
|
||||||
@Mock WebViewComponentTemplateCompileUnit compileUnit,
|
|
||||||
@Mock ModuleNode moduleNode
|
|
||||||
) {
|
|
||||||
final var source = "Hello, World!";
|
|
||||||
final var buildResult = this.build(source);
|
|
||||||
final var configuration = this.getConfiguration(compileUnit, moduleNode);
|
|
||||||
final var transpiler = this.getBodyTranspiler(configuration);
|
|
||||||
final var state = TranspilerState.withDefaultRootScope();
|
|
||||||
final var addOrAppend = configuration.getAppendOrAddStatementFactory();
|
|
||||||
final BlockStatement blockStatement = transpiler.transpileBody(
|
|
||||||
buildResult.bodyNode(),
|
|
||||||
(node, expression) -> addOrAppend.addOrAppend(node, state, ignored -> expression),
|
|
||||||
TranspilerState.withDefaultRootScope()
|
|
||||||
);
|
|
||||||
assertEquals(1, blockStatement.getStatements().size());
|
|
||||||
final var s0 = (ExpressionStatement) blockStatement.getStatements().getFirst();
|
|
||||||
final var binaryExpression = (MethodCallExpression) s0.getExpression();
|
|
||||||
final var args = (TupleExpression) binaryExpression.getArguments();
|
|
||||||
final var first = (ConstantExpression) args.getExpression(0);
|
|
||||||
assertEquals("Hello, World!", first.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,82 +0,0 @@
|
|||||||
package groowt.view.component.web.transpiler;
|
|
||||||
|
|
||||||
import groowt.view.component.web.antlr.ParserUtil;
|
|
||||||
import groowt.view.component.web.antlr.TokenList;
|
|
||||||
import groowt.view.component.web.ast.DefaultAstBuilder;
|
|
||||||
import groowt.view.component.web.ast.DefaultNodeFactory;
|
|
||||||
import groowt.view.component.web.ast.node.BodyNode;
|
|
||||||
import groowt.view.component.web.ast.node.GStringBodyTextNode;
|
|
||||||
import groowt.view.component.web.transpile.GStringTranspiler;
|
|
||||||
import org.codehaus.groovy.ast.expr.ClosureExpression;
|
|
||||||
import org.codehaus.groovy.ast.expr.GStringExpression;
|
|
||||||
import org.codehaus.groovy.ast.expr.VariableExpression;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
public abstract class GStringTranspilerTests {
|
|
||||||
|
|
||||||
protected abstract GStringTranspiler getGStringTranspiler();
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void smokeScreen() {
|
|
||||||
assertDoesNotThrow(() -> {
|
|
||||||
getGStringTranspiler();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected BodyNode getBodyNode(String source) {
|
|
||||||
final var parseResult = ParserUtil.parseCompilationUnit(source);
|
|
||||||
final var tokenList = new TokenList(parseResult.getTokenStream());
|
|
||||||
final var nodeFactory = new DefaultNodeFactory(tokenList);
|
|
||||||
final var astBuilder = new DefaultAstBuilder(nodeFactory);
|
|
||||||
final var cuNode = astBuilder.buildCompilationUnit(parseResult.getCompilationUnitContext());
|
|
||||||
return Objects.requireNonNull(cuNode.getBodyNode());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void doTest(String source, Consumer<GStringExpression> further) {
|
|
||||||
final var gStringBodyTextNode = this.getBodyNode(source).getAt(0, GStringBodyTextNode.class);
|
|
||||||
final var transpiler = this.getGStringTranspiler();
|
|
||||||
final GStringExpression gStringExpression = transpiler.createGStringExpression(gStringBodyTextNode);
|
|
||||||
assertEquals(source, gStringExpression.getText());
|
|
||||||
further.accept(gStringExpression);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void gStringExpressionWithDollarReference() {
|
|
||||||
this.doTest("Hello, $target!", gStringExpression -> {
|
|
||||||
assertEquals(2, gStringExpression.getStrings().size());
|
|
||||||
assertEquals(1, gStringExpression.getValues().size());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void multiplePathValues() {
|
|
||||||
this.doTest("$greeting, $target!", gStringExpression -> {
|
|
||||||
assertEquals(3, gStringExpression.getStrings().size());
|
|
||||||
assertEquals(2, gStringExpression.getValues().size());
|
|
||||||
final var firstValue = gStringExpression.getValue(0);
|
|
||||||
assertInstanceOf(VariableExpression.class, firstValue);
|
|
||||||
assertEquals("greeting", firstValue.getText());
|
|
||||||
final var secondValue = gStringExpression.getValue(1);
|
|
||||||
assertInstanceOf(VariableExpression.class, secondValue);
|
|
||||||
assertEquals("target", secondValue.getText());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void pathAndClosure() {
|
|
||||||
this.doTest("$greeting, ${consume(out)}!", gStringExpression -> {
|
|
||||||
assertEquals(3, gStringExpression.getStrings().size());
|
|
||||||
assertEquals(2, gStringExpression.getValues().size());
|
|
||||||
final var firstValue = gStringExpression.getValue(0);
|
|
||||||
assertInstanceOf(VariableExpression.class, firstValue);
|
|
||||||
assertEquals("greeting", firstValue.getText());
|
|
||||||
assertInstanceOf(ClosureExpression.class, gStringExpression.getValue(1));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user