Transpiler refactoring finished.

This commit is contained in:
JesseBrault0709 2024-05-27 09:02:53 +02:00
parent 2bac0f55dc
commit e112d81ea8
31 changed files with 287 additions and 861 deletions

View File

@ -361,7 +361,7 @@ public class DefaultAstBuilderVisitor extends WebViewComponentsParserBaseVisitor
public @Nullable Node visitPlainScriptlet(WebViewComponentsParser.PlainScriptletContext ctx) {
final TerminalNode groovyCode = ctx.GroovyCode();
if (groovyCode != null) {
return this.nodeFactory.plainScriptletNode(this.getTokenRange(ctx), groovyCode.getSymbol().getTokenIndex());
return this.nodeFactory.plainScriptletNode(this.getTokenRange(ctx), groovyCode.getSymbol().getText());
} else {
return null;
}
@ -381,16 +381,15 @@ public class DefaultAstBuilderVisitor extends WebViewComponentsParserBaseVisitor
}
@Override
public @Nullable Node visitDollarReference(WebViewComponentsParser.DollarReferenceContext ctx) {
final TerminalNode groovyCode = ctx.GroovyCode();
if (groovyCode != null) {
return this.nodeFactory.dollarReferenceNode(
this.getTokenRange(ctx),
groovyCode.getSymbol().getTokenIndex()
);
public Node visitDollarReference(WebViewComponentsParser.DollarReferenceContext ctx) {
final String groovyCode = ctx.GroovyCode().getText();
final List<String> parts = new ArrayList<>();
if (groovyCode.contains(".")) {
parts.addAll(List.of(groovyCode.split("\\.")));
} else {
return null;
parts.add(groovyCode);
}
return this.nodeFactory.dollarReferenceNode(this.getTokenRange(ctx), parts);
}
@Override

View File

@ -225,8 +225,8 @@ public class DefaultNodeFactory implements NodeFactory {
}
@Override
public PlainScriptletNode plainScriptletNode(TokenRange tokenRange, int groovyIndex) {
return this.objectFactory.get(PlainScriptletNode.class, tokenRange, groovyIndex);
public PlainScriptletNode plainScriptletNode(TokenRange tokenRange, String groovyCode) {
return this.objectFactory.get(PlainScriptletNode.class, tokenRange, groovyCode);
}
@Override
@ -235,8 +235,8 @@ public class DefaultNodeFactory implements NodeFactory {
}
@Override
public DollarReferenceNode dollarReferenceNode(TokenRange tokenRange, int groovyIndex) {
return this.objectFactory.get(DollarReferenceNode.class, tokenRange, groovyIndex);
public DollarReferenceNode dollarReferenceNode(TokenRange tokenRange, List<String> parts) {
return this.objectFactory.get(DollarReferenceNode.class, tokenRange, parts);
}
}

View File

@ -76,10 +76,10 @@ public interface NodeFactory {
EqualsScriptletNode equalsScriptletNode(TokenRange tokenRange, String groovyCode);
PlainScriptletNode plainScriptletNode(TokenRange tokenRange, int groovyIndex);
PlainScriptletNode plainScriptletNode(TokenRange tokenRange, String groovyCode);
DollarScriptletNode dollarScriptletNode(TokenRange tokenRange, String groovyCode);
DollarReferenceNode dollarReferenceNode(TokenRange tokenRange, int groovyIndex);
DollarReferenceNode dollarReferenceNode(TokenRange tokenRange, List<String> parts);
}

View File

@ -11,6 +11,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
@Deprecated
public class GroovyCodeNodeExtension implements NodeExtension {
private final Node self;

View File

@ -38,7 +38,7 @@ public class ClosureValueNode extends AbstractLeafNode implements ValueNode {
}
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() {

View File

@ -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));
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -1,22 +1,8 @@
package groowt.view.component.web.transpile;
import groowt.view.component.web.ast.node.BodyChildNode;
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.Statement;
public interface BodyTranspiler {
@FunctionalInterface
interface AddOrAppendCallback {
Statement createStatement(BodyChildNode source, Expression expression);
}
BlockStatement transpileBody(
BodyNode bodyNode,
AddOrAppendCallback addOrAppendCallback,
TranspilerState state
);
BlockStatement transpileBody(BodyNode bodyNode, TranspilerState state);
}

View File

@ -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));
}
}
}

View File

@ -33,16 +33,16 @@ public class DefaultBodyTextTranspiler implements BodyTextTranspiler {
this.includeComments = includeComments;
}
protected Statement handleStringLiteral(Token source) {
protected Statement handleStringLiteral(TranspilerState state, Token source) {
final ConstantExpression literal = getStringLiteral(source.getText());
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);
this.positionSetter.setPosition(literal, source);
return this.leftShiftFactory.create(literal);
return this.leftShiftFactory.create(state, literal);
}
protected List<Statement> handleHtmlCommentChild(HtmlCommentChild child, TranspilerState state) {
@ -67,23 +67,23 @@ public class DefaultBodyTextTranspiler implements BodyTextTranspiler {
final List<Statement> result = new ArrayList<>();
switch (child) {
case QuestionNode questionNode -> {
result.add(this.handleStringLiteral(questionNode.getOpenToken()));
result.add(this.handleStringLiteral(state, questionNode.getOpenToken()));
questionNode.getChildrenAsQuestionTagChildren().stream()
.map(questionChild -> this.handleQuestionTagChild(questionChild, state))
.forEach(result::addAll);
result.add(this.handleStringLiteral(questionNode.getCloseToken()));
result.add(this.handleStringLiteral(state, questionNode.getCloseToken()));
}
case HtmlCommentNode commentNode -> {
if (this.includeComments) {
result.add(this.handleStringLiteral(commentNode.getOpenToken()));
result.add(this.handleStringLiteral(state, commentNode.getOpenToken()));
commentNode.getChildrenAsHtmlCommentChildren().stream()
.map(commentChild -> this.handleHtmlCommentChild(commentChild, state))
.forEach(result::addAll);
result.add(this.handleStringLiteral(commentNode.getCloseToken()));
result.add(this.handleStringLiteral(state, commentNode.getCloseToken()));
}
}
case TextNode textNode -> {
result.add(this.handleStringLiteral(textNode, textNode.getContent()));
result.add(this.handleStringLiteral(state, textNode, textNode.getContent()));
}
case GroovyBodyNode groovyBodyNode -> {
result.add(this.groovyBodyNodeTranspiler.createGroovyBodyNodeStatements(groovyBodyNode, state));

View File

@ -18,11 +18,7 @@ public class DefaultBodyTranspiler implements BodyTranspiler {
}
@Override
public BlockStatement transpileBody(
BodyNode bodyNode,
AddOrAppendCallback addOrAppendCallback,
TranspilerState state
) {
public BlockStatement transpileBody(BodyNode bodyNode, TranspilerState state) {
final BlockStatement block = new BlockStatement();
block.setVariableScope(state.pushScope());
for (final Node child : bodyNode.getChildren()) {

View File

@ -1,6 +1,5 @@
package groowt.view.component.web.transpile;
import groowt.util.fp.provider.Provider;
import groowt.view.component.context.ComponentResolveException;
import groowt.view.component.runtime.ComponentCreateException;
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 isWithPackage = Pattern.compile("^\\p{Ll}.+\\.");
private final Provider<AppendOrAddStatementFactory> appendOrAddStatementFactoryProvider;
private final Provider<ComponentClassNodeResolver> componentClassNodeResolverProvider;
private final Provider<ValueNodeTranspiler> valueNodeTranspilerProvider;
private final Provider<BodyTranspiler> bodyTranspilerProvider;
private LeftShiftFactory leftShiftFactory;
private ValueNodeTranspiler valueNodeTranspiler;
private BodyTranspiler bodyTranspiler;
private ComponentClassNodeResolver componentClassNodeResolver;
public DefaultComponentTranspiler(
Provider<AppendOrAddStatementFactory> appendOrAddStatementFactoryProvider,
Provider<ComponentClassNodeResolver> componentClassNodeResolverProvider,
Provider<ValueNodeTranspiler> valueNodeTranspilerProvider,
Provider<BodyTranspiler> bodyTranspilerProvider
) {
this.appendOrAddStatementFactoryProvider = appendOrAddStatementFactoryProvider;
this.componentClassNodeResolverProvider = componentClassNodeResolverProvider;
this.valueNodeTranspilerProvider = valueNodeTranspilerProvider;
this.bodyTranspilerProvider = bodyTranspilerProvider;
public void setLeftShiftFactory(LeftShiftFactory leftShiftFactory) {
this.leftShiftFactory = leftShiftFactory;
}
protected ValueNodeTranspiler getValueNodeTranspiler() {
return this.valueNodeTranspilerProvider.get();
public void setValueNodeTranspiler(ValueNodeTranspiler valueNodeTranspiler) {
this.valueNodeTranspiler = valueNodeTranspiler;
}
protected BodyTranspiler getBodyTranspiler() {
return this.bodyTranspilerProvider.get();
public void setBodyTranspiler(BodyTranspiler bodyTranspiler) {
this.bodyTranspiler = bodyTranspiler;
}
protected AppendOrAddStatementFactory getAppendOrAddStatementFactory() {
return this.appendOrAddStatementFactoryProvider.get();
}
protected ComponentClassNodeResolver getComponentClassNodeResolver() {
return this.componentClassNodeResolverProvider.get();
public void setComponentClassNodeResolver(ComponentClassNodeResolver componentClassNodeResolver) {
this.componentClassNodeResolver = componentClassNodeResolver;
}
/* UTIL */
@ -104,7 +91,7 @@ public class DefaultComponentTranspiler implements ComponentTranspiler {
// we need to resolve it
final var isWithPackageMatcher = isWithPackage.matcher(identifier);
if (isWithPackageMatcher.matches()) {
final var resolveResult = this.getComponentClassNodeResolver().getClassForFqn(identifier);
final var resolveResult = this.componentClassNodeResolver.getClassForFqn(identifier);
if (resolveResult.isLeft()) {
final var error = resolveResult.getLeft();
error.setNode(componentNode.getArgs().getType());
@ -117,7 +104,7 @@ public class DefaultComponentTranspiler implements ComponentTranspiler {
}
} else {
final var resolveResult =
this.getComponentClassNodeResolver().getClassForNameWithoutPackage(identifier);
this.componentClassNodeResolver.getClassForNameWithoutPackage(identifier);
if (resolveResult.isLeft()) {
final var error = resolveResult.getLeft();
error.setNode(componentNode.getArgs().getType());
@ -260,7 +247,7 @@ public class DefaultComponentTranspiler implements ComponentTranspiler {
final Expression valueExpr = switch (attrNode) {
case BooleanValueAttrNode ignored -> ConstantExpression.PRIM_TRUE;
case KeyValueAttrNode keyValueAttrNode ->
this.getValueNodeTranspiler().createExpression(keyValueAttrNode.getValueNode(), state);
this.valueNodeTranspiler.createExpression(keyValueAttrNode.getValueNode(), state);
};
return new MapEntryExpression(keyExpr, valueExpr);
}
@ -328,11 +315,7 @@ public class DefaultComponentTranspiler implements ComponentTranspiler {
scope.putDeclaredVariable(childListParam);
state.pushChildList(childListParam);
final BlockStatement bodyStatements = this.getBodyTranspiler().transpileBody(
bodyNode,
(sourceNode, expr) -> this.getChildListAdd(childListParam, expr),
state
);
final BlockStatement bodyStatements = this.bodyTranspiler.transpileBody(bodyNode, state);
// clean up
state.popChildList();
@ -491,11 +474,7 @@ public class DefaultComponentTranspiler implements ComponentTranspiler {
// Create
final List<Statement> createStatements = this.getTypedCreateStatements(typedComponentNode, state);
// Append/Add
final Statement addOrAppend = this.getAppendOrAddStatementFactory().addOrAppend(
componentNode,
state,
(VariableExpression) state.getCurrentComponent()
);
final Statement leftShift = this.leftShiftFactory.create(state, state.getCurrentComponent());
// cleanup
state.popResolved();
@ -504,17 +483,16 @@ public class DefaultComponentTranspiler implements ComponentTranspiler {
final List<Statement> allStatements = new ArrayList<>();
allStatements.addAll(resolveStatements);
allStatements.addAll(createStatements);
allStatements.add(addOrAppend);
allStatements.add(leftShift);
return allStatements;
} else if (componentNode instanceof FragmentComponentNode fragmentComponentNode) {
// Create and add all at once
final Statement addOrAppend = this.getAppendOrAddStatementFactory().addOrAppend(
componentNode,
final Statement leftShift = this.leftShiftFactory.create(
state,
this.getFragmentCreateExpression(fragmentComponentNode, state)
);
return List.of(addOrAppend);
return List.of(leftShift);
} else {
throw new WebViewComponentBugError(new IllegalArgumentException(
"Cannot handle a ComponentNode not of type TypedComponentNode or FragmentComponentNode."

View File

@ -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;
}
}

View File

@ -3,7 +3,6 @@ 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;
@ -16,7 +15,6 @@ 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;
@ -58,7 +56,7 @@ public class DefaultGroovyBodyNodeTranspiler implements GroovyBodyNodeTranspiler
);
callExpr = new MethodCallExpression(cl, "call", argsList);
}
return this.leftShiftFactory.create(callExpr);
return this.leftShiftFactory.create(state, callExpr);
}
protected Statement handlePlainScriptlet(PlainScriptletNode plainScriptletNode, TranspilerState state) {
@ -76,7 +74,7 @@ public class DefaultGroovyBodyNodeTranspiler implements GroovyBodyNodeTranspiler
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 Expression toLeftShift;
if (cl.getParameters() == null) {
@ -89,10 +87,10 @@ public class DefaultGroovyBodyNodeTranspiler implements GroovyBodyNodeTranspiler
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;
PropertyExpression propertyExpr = null;
for (final String part : dollarReferenceNode.getParts()) {
@ -107,10 +105,10 @@ public class DefaultGroovyBodyNodeTranspiler implements GroovyBodyNodeTranspiler
final var positionVisitor = new PositionVisitor(this.positionSetter, dollarReferenceNode);
if (propertyExpr != null) {
propertyExpr.visit(positionVisitor);
return this.leftShiftFactory.create(propertyExpr);
return this.leftShiftFactory.create(state, propertyExpr);
} else if (root != null) {
root.visit(positionVisitor);
return this.leftShiftFactory.create(root);
return this.leftShiftFactory.create(state, root);
} else {
throw new WebViewComponentBugError("Did not expect root to be null.");
}
@ -121,8 +119,8 @@ public class DefaultGroovyBodyNodeTranspiler implements GroovyBodyNodeTranspiler
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);
case DollarScriptletNode dollarScriptletNode -> this.handleDollarScriptlet(dollarScriptletNode, state);
case DollarReferenceNode dollarReferenceNode -> this.handleDollarReference(dollarReferenceNode, state);
default -> throw new WebViewComponentBugError(new UnsupportedOperationException(
"GroovyBodyNode of type " + groovyBodyNode.getClass().getName() + " is not supported."
));

View File

@ -4,14 +4,12 @@ import groovy.transform.Field;
import groowt.view.component.compiler.ComponentTemplateCompileException;
import groowt.view.component.compiler.ComponentTemplateCompileUnit;
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.CompilationUnitNode;
import groowt.view.component.web.ast.node.PreambleNode;
import groowt.view.component.web.compiler.MultipleWebViewComponentCompileErrorsException;
import groowt.view.component.web.compiler.WebViewComponentTemplateCompileException;
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.resolve.ClassLoaderComponentClassNodeResolver;
import org.codehaus.groovy.ast.*;
@ -37,10 +35,9 @@ public class DefaultGroovyTranspiler implements GroovyTranspiler {
private static final ClassNode FIELD_ANNOTATION = ClassHelper.make(Field.class);
protected TranspilerConfiguration getConfiguration(
WebViewComponentTemplateCompileUnit compileUnit,
ClassLoader classLoader
ClassLoaderComponentClassNodeResolver classLoaderComponentClassNodeResolver
) {
return new DefaultTranspilerConfiguration(new ClassLoaderComponentClassNodeResolver(compileUnit, classLoader));
return SimpleTranspilerConfiguration.withDefaults(classLoaderComponentClassNodeResolver);
}
protected void addAutomaticImports(WebViewComponentModuleNode moduleNode, TranspilerConfiguration configuration) {
@ -235,20 +232,7 @@ public class DefaultGroovyTranspiler implements GroovyTranspiler {
TranspilerConfiguration transpilerConfiguration,
TranspilerState state
) {
final var appendOrAddStatementFactory = transpilerConfiguration.getAppendOrAddStatementFactory();
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);
return transpilerConfiguration.getBodyTranspiler().transpileBody(bodyNode, state);
}
@Override
@ -258,10 +242,12 @@ public class DefaultGroovyTranspiler implements GroovyTranspiler {
CompilationUnitNode compilationUnitNode,
String templateClassSimpleName
) throws ComponentTemplateCompileException {
// transpilerConfiguration and positionSetter
final var transpilerConfiguration = this.getConfiguration(
compileUnit, compileUnit.getGroovyCompilationUnit().getClassLoader()
// resolver, transpilerConfiguration, and positionSetter
final ClassLoaderComponentClassNodeResolver resolver = new ClassLoaderComponentClassNodeResolver(
compileUnit,
compileUnit.getGroovyCompilationUnit().getClassLoader()
);
final var transpilerConfiguration = this.getConfiguration(resolver);
final PositionSetter positionSetter = transpilerConfiguration.getPositionSetter();
// prepare sourceUnit
@ -280,6 +266,9 @@ public class DefaultGroovyTranspiler implements GroovyTranspiler {
compileUnit, sourceUnit, transpilerConfiguration
);
// set resolver's moduleNode
resolver.setModuleNode(moduleNode);
// prepare mainClassNode
final ClassNode mainClassNode = this.initMainClassNode(compileUnit, templateClassSimpleName, moduleNode);

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -1,6 +1,5 @@
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 groowt.view.component.web.transpile.groovy.GroovyUtil.ConvertResult;
@ -12,49 +11,39 @@ import org.codehaus.groovy.ast.stmt.ExpressionStatement;
import org.codehaus.groovy.ast.stmt.Statement;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import static groowt.view.component.web.transpile.TranspilerUtil.getStringLiteral;
// TODO: set positions
public class DefaultValueNodeTranspiler implements ValueNodeTranspiler {
private final ComponentTranspiler componentTranspiler;
private final PositionSetter positionSetter;
public DefaultValueNodeTranspiler(ComponentTranspiler componentTranspiler) {
public DefaultValueNodeTranspiler(ComponentTranspiler componentTranspiler, PositionSetter positionSetter) {
this.componentTranspiler = componentTranspiler;
this.positionSetter = positionSetter;
}
// TODO: positions
protected Expression handleClosureNode(ClosureValueNode closureValueNode) {
final var rawCode = closureValueNode.getGroovyCode().getAsValidGroovyCode();
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();
if (closureCode instanceof BlockStatement blockStatement) {
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();
return switch (expression) {
case ConstantExpression ignored -> expression;
case VariableExpression ignored -> expression;
case PropertyExpression ignored -> expression;
default -> convertedClosure;
};
} else {
throw new IllegalArgumentException("A component closure value must produce a value.");
}
} else {
return convertedClosure;
}
} else {
return convertedClosure;
if (closureCode instanceof ExpressionStatement expressionStatement) {
final Expression expression = expressionStatement.getExpression();
return switch (expression) {
case ConstantExpression ignored -> expression;
case VariableExpression ignored -> expression;
case PropertyExpression ignored -> expression;
default -> convertedClosure;
};
}
return convertedClosure;
}
private Expression gStringValue(GStringValueNode gStringValueNode) {
@ -65,16 +54,27 @@ public class DefaultValueNodeTranspiler implements ValueNodeTranspiler {
throw new IllegalStateException("block statement is null or empty");
}
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();
}
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) {
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) {
@ -84,7 +84,7 @@ public class DefaultValueNodeTranspiler implements ValueNodeTranspiler {
componentValueNode.getComponentNode(),
state
), state.getCurrentScope())
); // TODO: set pos
);
}
@Override

View File

@ -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);
}

View File

@ -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();
}

View File

@ -4,5 +4,5 @@ import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.stmt.Statement;
public interface LeftShiftFactory {
Statement create(Expression rightSide);
Statement create(TranspilerState state, Expression rightSide);
}

View File

@ -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;
}
}

View File

@ -9,7 +9,6 @@ import java.util.Set;
public interface TranspilerConfiguration {
PositionSetter getPositionSetter();
BodyTranspiler getBodyTranspiler();
AppendOrAddStatementFactory getAppendOrAddStatementFactory();
Map<String, ClassNode> getImports();
Set<String> getStarImports();
Set<Tuple3<ClassNode, String, String>> getStaticImports();

View File

@ -6,13 +6,12 @@ import groowt.view.component.web.antlr.TokenList
import groowt.view.component.web.antlr.WebViewComponentsLexer
import groowt.view.component.web.antlr.WebViewComponentsParser
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.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
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 {
@ -52,30 +51,4 @@ class DefaultAstBuilderVisitorTests {
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)
}
}
}
}
}
}

View File

@ -1,7 +1,7 @@
package groowt.view.component.web.transpiler;
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.resolve.CachingComponentClassNodeResolver;
import org.codehaus.groovy.ast.ModuleNode;
@ -13,9 +13,7 @@ public class DefaultBodyTranspilerTests extends BodyTranspilerTests {
WebViewComponentTemplateCompileUnit compileUnit,
ModuleNode moduleNode
) {
return new DefaultTranspilerConfiguration(
new CachingComponentClassNodeResolver(compileUnit)
);
return SimpleTranspilerConfiguration.withDefaults(new CachingComponentClassNodeResolver(compileUnit));
}
}

View File

@ -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));
}
}

View File

@ -223,7 +223,7 @@ public abstract class NodeFactoryTests {
@Test
public void plainScriptletNode() {
assertNotNull(this.nodeFactory.plainScriptletNode(this.getTokenRange(), 0));
assertNotNull(this.nodeFactory.plainScriptletNode(this.getTokenRange(), ""));
}
@Test
@ -233,7 +233,7 @@ public abstract class NodeFactoryTests {
@Test
public void dollarReferenceNode() {
assertNotNull(this.nodeFactory.dollarReferenceNode(this.getTokenRange(), 0));
assertNotNull(this.nodeFactory.dollarReferenceNode(this.getTokenRange(), List.of("test")));
}
}

View File

@ -8,13 +8,7 @@ import groowt.view.component.web.ast.node.BodyNode;
import groowt.view.component.web.compiler.WebViewComponentTemplateCompileUnit;
import groowt.view.component.web.transpile.BodyTranspiler;
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.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.extension.ExtendWith;
import org.mockito.Mock;
@ -30,9 +24,7 @@ public abstract class BodyTranspilerTests {
ModuleNode moduleNode
);
protected record BuildResult(BodyNode bodyNode, TokenList tokenList) {
}
protected record BuildResult(BodyNode bodyNode, TokenList tokenList) {}
protected BuildResult build(String 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());
}
}

View File

@ -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));
});
}
}