Some gString/closure transpilation fixing, moving groovy util, and star imports.
This commit is contained in:
parent
a2c6b787f7
commit
494da75fe9
@ -1,16 +1,19 @@
|
|||||||
package groowt.view.web.ast;
|
package groowt.view.web.ast;
|
||||||
|
|
||||||
import groowt.view.web.antlr.TokenList;
|
import groowt.view.web.antlr.TokenList;
|
||||||
|
import groowt.view.web.ast.extension.NodeExtension;
|
||||||
import groowt.view.web.ast.node.LeafNode;
|
import groowt.view.web.ast.node.LeafNode;
|
||||||
import groowt.view.web.ast.node.Node;
|
import groowt.view.web.ast.node.Node;
|
||||||
import groowt.view.web.ast.node.TreeNode;
|
import groowt.view.web.ast.node.TreeNode;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
public final class NodeUtil {
|
public final class NodeUtil {
|
||||||
|
|
||||||
public static boolean isAnyOfType(Node subject, Class<?>... nodeTypes) {
|
public static boolean isAnyOfType(Node subject, Class<?>... nodeTypes) {
|
||||||
|
Objects.requireNonNull(subject);
|
||||||
for (final var type : nodeTypes) {
|
for (final var type : nodeTypes) {
|
||||||
if (type.isAssignableFrom(subject.getClass())) {
|
if (type.isAssignableFrom(subject.getClass())) {
|
||||||
return true;
|
return true;
|
||||||
@ -19,7 +22,19 @@ public final class NodeUtil {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static boolean hasExtensionOfType(Node subject, Class<?>... extensionTypes) {
|
||||||
|
Objects.requireNonNull(subject);
|
||||||
|
for (final var extensionType : extensionTypes) {
|
||||||
|
if (subject.hasExtension((Class<? extends NodeExtension>) extensionType)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isAnyOfType(Node subject, List<Class<? extends Node>> nodeTypes) {
|
public static boolean isAnyOfType(Node subject, List<Class<? extends Node>> nodeTypes) {
|
||||||
|
Objects.requireNonNull(subject);
|
||||||
for (final var type : nodeTypes) {
|
for (final var type : nodeTypes) {
|
||||||
if (type.isAssignableFrom(subject.getClass())) {
|
if (type.isAssignableFrom(subject.getClass())) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -38,7 +38,7 @@ public class ClosureValueNode extends AbstractLeafNode implements ValueNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected String toValidGroovyCode(List<Token> groovyTokens) {
|
protected String toValidGroovyCode(List<Token> groovyTokens) {
|
||||||
return "{ " + groovyTokens.stream().map(Token::getText).collect(Collectors.joining()) + "\n}";
|
return "def c = { " + groovyTokens.stream().map(Token::getText).collect(Collectors.joining()) + "\n}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public GroovyCodeNodeExtension getGroovyCode() {
|
public GroovyCodeNodeExtension getGroovyCode() {
|
||||||
|
@ -4,9 +4,9 @@ import groowt.view.component.context.ComponentResolveException;
|
|||||||
import groowt.view.component.runtime.ComponentCreateException;
|
import groowt.view.component.runtime.ComponentCreateException;
|
||||||
import groowt.view.web.WebViewComponentBugError;
|
import groowt.view.web.WebViewComponentBugError;
|
||||||
import groowt.view.web.ast.node.*;
|
import groowt.view.web.ast.node.*;
|
||||||
|
import groowt.view.web.transpile.groovy.GroovyUtil;
|
||||||
|
import groowt.view.web.transpile.groovy.GroovyUtil.ConvertResult;
|
||||||
import groowt.view.web.transpile.resolve.ComponentClassNodeResolver;
|
import groowt.view.web.transpile.resolve.ComponentClassNodeResolver;
|
||||||
import groowt.view.web.transpile.util.GroovyUtil;
|
|
||||||
import groowt.view.web.transpile.util.GroovyUtil.ConvertResult;
|
|
||||||
import groowt.view.web.util.Provider;
|
import groowt.view.web.util.Provider;
|
||||||
import groowt.view.web.util.SourcePosition;
|
import groowt.view.web.util.SourcePosition;
|
||||||
import org.codehaus.groovy.ast.*;
|
import org.codehaus.groovy.ast.*;
|
||||||
@ -353,14 +353,8 @@ public class DefaultComponentTranspiler implements ComponentTranspiler {
|
|||||||
) {
|
) {
|
||||||
final var createArgs = new ArgumentListExpression();
|
final var createArgs = new ArgumentListExpression();
|
||||||
|
|
||||||
final VariableExpression resolvedVariableExpression;
|
final VariableExpression currentResolved = state.getCurrentResolved();
|
||||||
final Variable currentResolved = state.getCurrentResolved();
|
createArgs.addExpression(currentResolved);
|
||||||
if (currentResolved instanceof VariableExpression) {
|
|
||||||
resolvedVariableExpression = (VariableExpression) currentResolved;
|
|
||||||
} else {
|
|
||||||
resolvedVariableExpression = new VariableExpression(currentResolved);
|
|
||||||
}
|
|
||||||
createArgs.addExpression(resolvedVariableExpression);
|
|
||||||
|
|
||||||
final List<AttrNode> attrNodes = componentNode.getArgs().getAttributes();
|
final List<AttrNode> attrNodes = componentNode.getArgs().getAttributes();
|
||||||
if (attrNodes.isEmpty()) {
|
if (attrNodes.isEmpty()) {
|
||||||
|
@ -8,7 +8,7 @@ import groowt.view.web.ast.extension.GStringScriptletExtension;
|
|||||||
import groowt.view.web.ast.node.GStringBodyTextNode;
|
import groowt.view.web.ast.node.GStringBodyTextNode;
|
||||||
import groowt.view.web.ast.node.JStringBodyTextNode;
|
import groowt.view.web.ast.node.JStringBodyTextNode;
|
||||||
import groowt.view.web.ast.node.Node;
|
import groowt.view.web.ast.node.Node;
|
||||||
import groowt.view.web.transpile.util.GroovyUtil;
|
import groowt.view.web.transpile.groovy.GroovyUtil;
|
||||||
import groowt.view.web.util.FilteringIterable;
|
import groowt.view.web.util.FilteringIterable;
|
||||||
import groowt.view.web.util.Option;
|
import groowt.view.web.util.Option;
|
||||||
import groowt.view.web.util.TokenRange;
|
import groowt.view.web.util.TokenRange;
|
||||||
@ -46,15 +46,11 @@ public class DefaultGStringTranspiler implements GStringTranspiler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Option<ConstantExpression> checkNextAfterDollar(Node current, @Nullable Node next) {
|
protected Option<ConstantExpression> checkNextAfterDollar(@Nullable Node next) {
|
||||||
if (!(next instanceof JStringBodyTextNode)) {
|
if (next != null && next.hasExtension(GStringNodeExtension.class)) {
|
||||||
return Option.liftLazy(() -> {
|
return Option.liftLazy(() -> {
|
||||||
final ConstantExpression expression = this.jStringTranspiler.createEmptyStringLiteral();
|
final ConstantExpression expression = this.jStringTranspiler.createEmptyStringLiteral();
|
||||||
if (next != null) {
|
|
||||||
this.positionSetter.setToStartOf(expression, next);
|
this.positionSetter.setToStartOf(expression, next);
|
||||||
} else {
|
|
||||||
this.positionSetter.setToStartOf(expression, current);
|
|
||||||
}
|
|
||||||
return expression;
|
return expression;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -114,13 +110,13 @@ public class DefaultGStringTranspiler implements GStringTranspiler {
|
|||||||
return new PathResult(
|
return new PathResult(
|
||||||
propertyExpression,
|
propertyExpression,
|
||||||
this.checkPrevBeforeDollar(prev, current),
|
this.checkPrevBeforeDollar(prev, current),
|
||||||
this.checkNextAfterDollar(current, next)
|
this.checkNextAfterDollar(next)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return new PathResult(
|
return new PathResult(
|
||||||
begin,
|
begin,
|
||||||
this.checkPrevBeforeDollar(prev, current),
|
this.checkPrevBeforeDollar(prev, current),
|
||||||
this.checkNextAfterDollar(current, next)
|
this.checkNextAfterDollar(next)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -187,13 +183,13 @@ public class DefaultGStringTranspiler implements GStringTranspiler {
|
|||||||
case GStringScriptletExtension scriptlet -> {
|
case GStringScriptletExtension scriptlet -> {
|
||||||
checkPrevBeforeDollar(prev, current).ifPresent(texts::add);
|
checkPrevBeforeDollar(prev, current).ifPresent(texts::add);
|
||||||
values.add(this.handleScriptlet(scriptlet));
|
values.add(this.handleScriptlet(scriptlet));
|
||||||
checkNextAfterDollar(current, next).ifPresent(texts::add);
|
checkNextAfterDollar(next).ifPresent(texts::add);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (texts.size() != values.size() + 1) {
|
if (!(texts.size() == values.size() || texts.size() == values.size() + 1)) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"incorrect amount of texts vs. values: " + texts.size() + " " + values.size()
|
"incorrect amount of texts vs. values: " + texts.size() + " " + values.size()
|
||||||
);
|
);
|
||||||
|
@ -11,8 +11,8 @@ import groowt.view.web.compiler.MultipleWebViewComponentCompileErrorsException;
|
|||||||
import groowt.view.web.compiler.WebViewComponentTemplateCompileException;
|
import groowt.view.web.compiler.WebViewComponentTemplateCompileException;
|
||||||
import groowt.view.web.compiler.WebViewComponentTemplateCompileUnit;
|
import groowt.view.web.compiler.WebViewComponentTemplateCompileUnit;
|
||||||
import groowt.view.web.runtime.DefaultWebViewRenderContext;
|
import groowt.view.web.runtime.DefaultWebViewRenderContext;
|
||||||
|
import groowt.view.web.transpile.groovy.GroovyUtil;
|
||||||
import groowt.view.web.transpile.resolve.ClassLoaderComponentClassNodeResolver;
|
import groowt.view.web.transpile.resolve.ClassLoaderComponentClassNodeResolver;
|
||||||
import groowt.view.web.transpile.util.GroovyUtil;
|
|
||||||
import org.codehaus.groovy.ast.*;
|
import org.codehaus.groovy.ast.*;
|
||||||
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;
|
||||||
|
@ -1,18 +1,20 @@
|
|||||||
package groowt.view.web.transpile;
|
package groowt.view.web.transpile;
|
||||||
|
|
||||||
|
import groowt.view.web.WebViewComponentBugError;
|
||||||
import groowt.view.web.ast.node.*;
|
import groowt.view.web.ast.node.*;
|
||||||
import groowt.view.web.transpile.TranspilerUtil.TranspilerState;
|
import groowt.view.web.transpile.TranspilerUtil.TranspilerState;
|
||||||
import groowt.view.web.transpile.util.GroovyUtil;
|
import groowt.view.web.transpile.groovy.GroovyUtil;
|
||||||
import groowt.view.web.transpile.util.GroovyUtil.ConvertResult;
|
import groowt.view.web.transpile.groovy.GroovyUtil.ConvertResult;
|
||||||
import org.codehaus.groovy.ast.Parameter;
|
import org.codehaus.groovy.ast.Parameter;
|
||||||
import org.codehaus.groovy.ast.expr.ClosureExpression;
|
import org.codehaus.groovy.ast.expr.*;
|
||||||
import org.codehaus.groovy.ast.expr.ConstantExpression;
|
|
||||||
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.EmptyStatement;
|
import org.codehaus.groovy.ast.stmt.EmptyStatement;
|
||||||
import org.codehaus.groovy.ast.stmt.ExpressionStatement;
|
import org.codehaus.groovy.ast.stmt.ExpressionStatement;
|
||||||
|
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.web.transpile.TranspilerUtil.getStringLiteral;
|
import static groowt.view.web.transpile.TranspilerUtil.getStringLiteral;
|
||||||
|
|
||||||
// TODO: set positions
|
// TODO: set positions
|
||||||
@ -24,16 +26,36 @@ public class DefaultValueNodeTranspiler implements ValueNodeTranspiler {
|
|||||||
this.componentTranspiler = componentTranspiler;
|
this.componentTranspiler = componentTranspiler;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ClosureExpression closureValue(ClosureValueNode closureValueNode) {
|
// TODO: positions
|
||||||
|
protected Expression handleClosureNode(ClosureValueNode closureValueNode) {
|
||||||
final var rawCode = closureValueNode.getGroovyCode().getAsValidGroovyCode();
|
final var rawCode = closureValueNode.getGroovyCode().getAsValidGroovyCode();
|
||||||
final ConvertResult convertResult = GroovyUtil.convert(rawCode);
|
final ClosureExpression convertedClosure = GroovyUtil.getClosure(rawCode);
|
||||||
final @Nullable BlockStatement blockStatement = convertResult.blockStatement();
|
final Statement closureCode = convertedClosure.getCode();
|
||||||
if (blockStatement == null || blockStatement.isEmpty()) {
|
if (closureCode instanceof BlockStatement blockStatement) {
|
||||||
throw new IllegalStateException("block statement is null or empty");
|
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;
|
||||||
}
|
}
|
||||||
final ExpressionStatement exprStmt = (ExpressionStatement) blockStatement.getStatements().getFirst();
|
|
||||||
// TODO: set pos
|
|
||||||
return (ClosureExpression) exprStmt.getExpression();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Expression gStringValue(GStringValueNode gStringValueNode) {
|
private Expression gStringValue(GStringValueNode gStringValueNode) {
|
||||||
@ -69,7 +91,7 @@ public class DefaultValueNodeTranspiler implements ValueNodeTranspiler {
|
|||||||
@Override
|
@Override
|
||||||
public Expression createExpression(ValueNode valueNode, TranspilerState state) {
|
public Expression createExpression(ValueNode valueNode, TranspilerState state) {
|
||||||
return switch (valueNode) {
|
return switch (valueNode) {
|
||||||
case ClosureValueNode closureValueNode -> this.closureValue(closureValueNode);
|
case ClosureValueNode closureValueNode -> this.handleClosureNode(closureValueNode);
|
||||||
case GStringValueNode gStringValueNode -> this.gStringValue(gStringValueNode);
|
case GStringValueNode gStringValueNode -> this.gStringValue(gStringValueNode);
|
||||||
case JStringValueNode jStringValueNode -> this.jStringValue(jStringValueNode);
|
case JStringValueNode jStringValueNode -> this.jStringValue(jStringValueNode);
|
||||||
case EmptyClosureValueNode emptyClosureValueNode -> this.emptyClosureValue(emptyClosureValueNode);
|
case EmptyClosureValueNode emptyClosureValueNode -> this.emptyClosureValue(emptyClosureValueNode);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package groowt.view.web.transpile;
|
package groowt.view.web.transpile;
|
||||||
|
|
||||||
|
import org.codehaus.groovy.ast.AnnotationNode;
|
||||||
import org.codehaus.groovy.ast.ClassNode;
|
import org.codehaus.groovy.ast.ClassNode;
|
||||||
import org.codehaus.groovy.ast.ImportNode;
|
import org.codehaus.groovy.ast.ImportNode;
|
||||||
import org.codehaus.groovy.ast.ModuleNode;
|
import org.codehaus.groovy.ast.ModuleNode;
|
||||||
@ -55,9 +56,16 @@ public class WebViewComponentModuleNode extends ModuleNode {
|
|||||||
.orElse(null);
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param alias the name of interest
|
||||||
|
* @return a standard (non-static, non-star) import, or {@code null} if there is none
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public @Nullable ImportNode getImport(String alias) {
|
public @Nullable ImportNode getImport(String alias) {
|
||||||
return this.allImports.get(alias);
|
return this.imports.stream()
|
||||||
|
.filter(importNode -> importNode.getAlias().equals(alias))
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void putToAll(String alias, ImportNode importNode) {
|
protected void putToAll(String alias, ImportNode importNode) {
|
||||||
@ -88,4 +96,52 @@ public class WebViewComponentModuleNode extends ModuleNode {
|
|||||||
this.putToAll(alias, importNode);
|
this.putToAll(alias, importNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addImport(String alias, ClassNode type) {
|
||||||
|
this.addImport(new ImportNode(type, alias));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addImport(String alias, ClassNode type, List<AnnotationNode> annotations) {
|
||||||
|
final var importNode = new ImportNode(type, alias);
|
||||||
|
importNode.addAnnotations(annotations);
|
||||||
|
this.addImport(importNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addStarImport(String packageName) {
|
||||||
|
this.addStarImport(new ImportNode(packageName));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addStarImport(String packageName, List<AnnotationNode> annotations) {
|
||||||
|
final var importNode = new ImportNode(packageName);
|
||||||
|
importNode.addAnnotations(annotations);
|
||||||
|
this.addStarImport(importNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addStaticImport(ClassNode type, String fieldName, String alias) {
|
||||||
|
this.addStaticImport(alias, new ImportNode(type, fieldName, alias));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addStaticImport(ClassNode type, String fieldName, String alias, List<AnnotationNode> annotations) {
|
||||||
|
final var importNode = new ImportNode(type, fieldName, alias);
|
||||||
|
importNode.addAnnotations(annotations);
|
||||||
|
this.addStaticImport(alias, importNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addStaticStarImport(String name, ClassNode type) {
|
||||||
|
this.addStaticStarImport(name, new ImportNode(type, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addStaticStarImport(String name, ClassNode type, List<AnnotationNode> annotations) {
|
||||||
|
final var importNode = new ImportNode(type, name);
|
||||||
|
importNode.addAnnotations(annotations);
|
||||||
|
this.addStaticStarImport(name, importNode);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package groowt.view.web.transpile.util;
|
package groowt.view.web.transpile.groovy;
|
||||||
|
|
||||||
import org.codehaus.groovy.ast.*;
|
import org.codehaus.groovy.ast.*;
|
||||||
import org.codehaus.groovy.ast.expr.*;
|
import org.codehaus.groovy.ast.expr.*;
|
@ -1,11 +1,15 @@
|
|||||||
package groowt.view.web.transpile.util;
|
package groowt.view.web.transpile.groovy;
|
||||||
|
|
||||||
import groovy.lang.GroovyCodeSource;
|
import groovy.lang.GroovyCodeSource;
|
||||||
|
import groowt.view.web.WebViewComponentBugError;
|
||||||
import org.codehaus.groovy.ast.ASTNode;
|
import org.codehaus.groovy.ast.ASTNode;
|
||||||
import org.codehaus.groovy.ast.ClassNode;
|
import org.codehaus.groovy.ast.ClassNode;
|
||||||
import org.codehaus.groovy.ast.ModuleNode;
|
import org.codehaus.groovy.ast.ModuleNode;
|
||||||
import org.codehaus.groovy.ast.builder.AstStringCompiler;
|
import org.codehaus.groovy.ast.builder.AstStringCompiler;
|
||||||
|
import org.codehaus.groovy.ast.expr.BinaryExpression;
|
||||||
|
import org.codehaus.groovy.ast.expr.ClosureExpression;
|
||||||
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.control.CompilationUnit;
|
import org.codehaus.groovy.control.CompilationUnit;
|
||||||
import org.codehaus.groovy.control.CompilePhase;
|
import org.codehaus.groovy.control.CompilePhase;
|
||||||
import org.codehaus.groovy.control.CompilerConfiguration;
|
import org.codehaus.groovy.control.CompilerConfiguration;
|
||||||
@ -97,6 +101,28 @@ public final class GroovyUtil {
|
|||||||
return new ConvertResult(moduleNode, blockStatement, scriptClassNode, classNodes);
|
return new ConvertResult(moduleNode, blockStatement, scriptClassNode, classNodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param source must be of form {@code def c = { ... }} and the closure must not be empty.
|
||||||
|
* @return the block inside the closure
|
||||||
|
*/
|
||||||
|
public static ClosureExpression getClosure(String source) {
|
||||||
|
final ConvertResult convertResult = convert(source);
|
||||||
|
final BlockStatement blockStatement = convertResult.blockStatement();
|
||||||
|
if (blockStatement == null) {
|
||||||
|
throw new WebViewComponentBugError(new IllegalArgumentException(
|
||||||
|
"Did not expect the source to produce no BlockStatement."
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if (blockStatement.isEmpty()) {
|
||||||
|
throw new WebViewComponentBugError(new IllegalArgumentException(
|
||||||
|
"Did not expect the BlockStatement to be empty."
|
||||||
|
));
|
||||||
|
}
|
||||||
|
final ExpressionStatement exprStmt = (ExpressionStatement) blockStatement.getStatements().getFirst();
|
||||||
|
final BinaryExpression binaryExpression = (BinaryExpression) exprStmt.getExpression();
|
||||||
|
return (ClosureExpression) binaryExpression.getRightExpression();
|
||||||
|
}
|
||||||
|
|
||||||
private GroovyUtil() {}
|
private GroovyUtil() {}
|
||||||
|
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package groowt.view.web.transpile.util
|
package groowt.view.web.transpile.groovy
|
||||||
|
|
||||||
import org.codehaus.groovy.ast.ASTNode
|
import org.codehaus.groovy.ast.ASTNode
|
||||||
|
|
@ -20,18 +20,34 @@ public class ModuleNodeComponentClassNodeResolver extends CachingComponentClassN
|
|||||||
@Override
|
@Override
|
||||||
public Either<ClassNodeResolveException, ClassNode> getClassForNameWithoutPackage(String nameWithoutPackage) {
|
public Either<ClassNodeResolveException, ClassNode> getClassForNameWithoutPackage(String nameWithoutPackage) {
|
||||||
return super.getClassForNameWithoutPackage(nameWithoutPackage).flatMapLeft(ignored -> {
|
return super.getClassForNameWithoutPackage(nameWithoutPackage).flatMapLeft(ignored -> {
|
||||||
// try imports first
|
// try regular imports first
|
||||||
final var importedClassNode = this.moduleNode.getImportType(nameWithoutPackage);
|
final var importedClassNode = this.moduleNode.getImportType(nameWithoutPackage);
|
||||||
if (importedClassNode != null) {
|
if (importedClassNode != null) {
|
||||||
this.addClassNode(importedClassNode);
|
this.addClassNode(importedClassNode);
|
||||||
return Either.right(importedClassNode);
|
return Either.right(importedClassNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// try star imports
|
||||||
|
final var starImports = this.moduleNode.getStarImports();
|
||||||
|
for (final var starImport : starImports) {
|
||||||
|
final var packageName = starImport.getPackageName();
|
||||||
|
final String fqn;
|
||||||
|
if (packageName.endsWith(".")) {
|
||||||
|
fqn = packageName + nameWithoutPackage;
|
||||||
|
} else {
|
||||||
|
fqn = packageName + "." + nameWithoutPackage;
|
||||||
|
}
|
||||||
|
final var withPackage = this.getClassForFqn(fqn);
|
||||||
|
if (withPackage.isRight()) {
|
||||||
|
return withPackage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// try pre-pending package and asking for fqn
|
// try pre-pending package and asking for fqn
|
||||||
final var packageName = this.moduleNode.getPackageName();
|
final var packageName = this.moduleNode.getPackageName();
|
||||||
final String fqn;
|
final String fqn;
|
||||||
if (packageName.endsWith(".")) {
|
if (packageName.endsWith(".")) {
|
||||||
fqn = this.moduleNode + nameWithoutPackage;
|
fqn = this.moduleNode.getPackageName() + nameWithoutPackage;
|
||||||
} else {
|
} else {
|
||||||
fqn = this.moduleNode.getPackageName() + "." + nameWithoutPackage;
|
fqn = this.moduleNode.getPackageName() + "." + nameWithoutPackage;
|
||||||
}
|
}
|
||||||
|
@ -7,17 +7,13 @@ class BaseWebViewComponentTests extends AbstractWebViewComponentTests {
|
|||||||
|
|
||||||
static final class Greeter extends BaseWebViewComponent {
|
static final class Greeter extends BaseWebViewComponent {
|
||||||
|
|
||||||
private final String target
|
final String target
|
||||||
|
|
||||||
Greeter(Map<String, Object> attr) {
|
Greeter(Map<String, Object> attr) {
|
||||||
super('Hello, $target!')
|
super('Hello, $target!')
|
||||||
this.target = Objects.requireNonNull(attr.get("target"))
|
this.target = Objects.requireNonNull(attr.get("target"))
|
||||||
}
|
}
|
||||||
|
|
||||||
String getTarget() {
|
|
||||||
return this.target
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class UsingGreeter extends BaseWebViewComponent {
|
static final class UsingGreeter extends BaseWebViewComponent {
|
||||||
|
@ -0,0 +1,51 @@
|
|||||||
|
package groowt.view.web
|
||||||
|
|
||||||
|
import groowt.view.web.lib.AbstractWebViewComponentTests
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
|
class SimpleWebViewComponentTests extends AbstractWebViewComponentTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void closureValueWithConstantExpressionEvaluatesToValue() {
|
||||||
|
this.doTest('<Echo greeting={"Hello, World!"}>$greeting</Echo>', 'Hello, World!')
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void closureValueWithVariableExpressionEvaluatesToValue() {
|
||||||
|
this.doTest(
|
||||||
|
'<Echo greeting="Hello, World!"><Echo subGreeting={greeting}>$subGreeting</Echo></Echo>',
|
||||||
|
'Hello, World!'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void closureWithPropertyExpressionEvaluatesToValue() {
|
||||||
|
this.doTest(
|
||||||
|
'''
|
||||||
|
---
|
||||||
|
import groovy.transform.Field
|
||||||
|
|
||||||
|
@Field
|
||||||
|
Map greetings = [hello: 'Hello!']
|
||||||
|
---
|
||||||
|
<Echo greeting={greetings.hello}>$greeting</Echo>
|
||||||
|
'''.stripIndent().trim(),
|
||||||
|
'Hello!'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void closureWithMethodCallIsClosure() {
|
||||||
|
this.doTest(
|
||||||
|
'''
|
||||||
|
---
|
||||||
|
def helper(String input) {
|
||||||
|
input.capitalize()
|
||||||
|
}
|
||||||
|
---
|
||||||
|
<Echo subHelper={ helper('lowercase') }>${ -> subHelper.call() }</Echo>
|
||||||
|
'''.stripIndent().trim(), 'Lowercase'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,9 +1,9 @@
|
|||||||
package groowt.view.web.tools
|
package groowt.view.web.tools
|
||||||
|
|
||||||
import groowt.view.web.transpile.util.GroovyUtil
|
import groowt.view.web.transpile.groovy.GroovyUtil
|
||||||
import org.codehaus.groovy.ast.ImportNode
|
import org.codehaus.groovy.ast.ImportNode
|
||||||
|
|
||||||
import static groowt.view.web.transpile.util.GroovyUtil.formatGroovy
|
import static groowt.view.web.transpile.groovy.GroovyUtil.formatGroovy
|
||||||
|
|
||||||
def src = '''
|
def src = '''
|
||||||
import some.Thing
|
import some.Thing
|
||||||
|
Loading…
Reference in New Issue
Block a user