GString transpilation: values in correct order, verbatim fixed.
This commit is contained in:
parent
cd63bd60a7
commit
5c3d973c4c
@ -144,11 +144,12 @@ def toolSpec = { String name, String mainClass ->
|
||||
}
|
||||
|
||||
final List<ToolSpec> toolSpecs = [
|
||||
toolSpec('parseTreeFileMaker', 'ParseTreeFileMakerCli'),
|
||||
toolSpec('astFileMaker', 'AstFileMakerCli'),
|
||||
toolSpec('convertToGroovy', 'ConvertToGroovy'),
|
||||
toolSpec('lexer', 'LexerTool'),
|
||||
toolSpec('parser', 'ParserTool')
|
||||
toolSpec('parser', 'ParserTool'),
|
||||
toolSpec('parseTreeFileMaker', 'ParseTreeFileMakerCli'),
|
||||
toolSpec('runTemplate', 'RunTemplate')
|
||||
]
|
||||
|
||||
toolSpecs.each { spec ->
|
||||
|
1
web-views/sketching/.gitignore
vendored
1
web-views/sketching/.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
*.groovy
|
||||
classes
|
||||
.txt
|
||||
|
@ -1,9 +1,10 @@
|
||||
---
|
||||
import groowt.view.component.ComponentContext
|
||||
|
||||
class Helper {
|
||||
String greeting
|
||||
void consume(out) {
|
||||
out << 'World'
|
||||
}
|
||||
|
||||
@groovy.transform.Field
|
||||
String greeting = 'Hello'
|
||||
---
|
||||
Hello, $target!
|
||||
$greeting, ${consume(out)}!
|
||||
What a nice day.
|
||||
|
@ -13,9 +13,4 @@ public non-sealed class GStringScriptletExtension extends GStringNodeExtension {
|
||||
super(self, allTokens.getRange(rawTokenRange));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAsValidEmbeddableCode() {
|
||||
return "${" + super.getAsValidEmbeddableCode() + "}";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,26 +1,46 @@
|
||||
package groowt.view.web.ast.node;
|
||||
|
||||
import groowt.util.di.annotation.Given;
|
||||
import groowt.view.web.antlr.TokenList;
|
||||
import groowt.view.web.ast.extension.GroovyCodeNodeExtension;
|
||||
import groowt.view.web.ast.extension.NodeExtensionContainer;
|
||||
import groowt.view.web.util.TokenRange;
|
||||
import jakarta.inject.Inject;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class PreambleNode extends AbstractLeafNode {
|
||||
|
||||
private final int groovyIndex;
|
||||
private final GroovyCodeNodeExtension groovyCode;
|
||||
|
||||
@Inject
|
||||
public PreambleNode(
|
||||
TokenList tokenList,
|
||||
NodeExtensionContainer extensionContainer,
|
||||
@Given TokenRange tokenRange,
|
||||
@Given int groovyCodeIndex
|
||||
) {
|
||||
super(tokenRange, extensionContainer);
|
||||
this.groovyIndex = groovyCodeIndex;
|
||||
this.groovyCode = this.createGroovyCode(tokenList, groovyCodeIndex);
|
||||
}
|
||||
|
||||
public int getGroovyCodeIndex() {
|
||||
return this.groovyIndex;
|
||||
protected GroovyCodeNodeExtension createGroovyCode(TokenList tokenList, int groovyCodeIndex) {
|
||||
return this.createExtension(
|
||||
GroovyCodeNodeExtension.class,
|
||||
TokenRange.fromIndex(tokenList, groovyCodeIndex),
|
||||
(Function<? super List<Token>, String>) this::toValidGroovyCode
|
||||
);
|
||||
}
|
||||
|
||||
protected String toValidGroovyCode(List<Token> tokenList) {
|
||||
return tokenList.stream().map(Token::getText).collect(Collectors.joining());
|
||||
}
|
||||
|
||||
public GroovyCodeNodeExtension getGroovyCode() {
|
||||
return this.groovyCode;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -24,16 +24,12 @@ public class WebViewComponentWriter {
|
||||
}
|
||||
|
||||
public void append(GString gString) {
|
||||
final String content;
|
||||
try {
|
||||
content = gString.toString();
|
||||
} catch (Exception exception) {
|
||||
throw new ComponentRenderException(exception);
|
||||
}
|
||||
try {
|
||||
this.delegate.append(content);
|
||||
gString.writeTo(this.delegate);
|
||||
} catch (IOException ioException) {
|
||||
throw new RuntimeException(ioException);
|
||||
} catch (Exception exception) {
|
||||
throw new ComponentRenderException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,4 +75,13 @@ public class WebViewComponentWriter {
|
||||
}
|
||||
}
|
||||
|
||||
public void leftShift(Object object) {
|
||||
switch (object) {
|
||||
case String s -> this.append(s);
|
||||
case GString gs -> this.append(gs);
|
||||
case ViewComponent viewComponent -> this.append(viewComponent);
|
||||
default -> this.append(object);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import groowt.view.web.ast.extension.GStringScriptletExtension;
|
||||
import groowt.view.web.ast.node.GStringBodyTextNode;
|
||||
import groowt.view.web.ast.node.JStringBodyTextNode;
|
||||
import groowt.view.web.ast.node.Node;
|
||||
import groowt.view.web.transpile.util.GroovyUtil;
|
||||
import groowt.view.web.util.FilteringIterable;
|
||||
import groowt.view.web.util.Option;
|
||||
import groowt.view.web.util.TokenRange;
|
||||
@ -15,6 +16,9 @@ import jakarta.inject.Inject;
|
||||
import jakarta.inject.Singleton;
|
||||
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;
|
||||
@ -70,7 +74,8 @@ public class DefaultGStringTranspiler implements GStringTranspiler {
|
||||
}
|
||||
|
||||
protected record PathResult(
|
||||
Expression result, Option<ConstantExpression> before,
|
||||
Expression result,
|
||||
Option<ConstantExpression> before,
|
||||
Option<ConstantExpression> after
|
||||
) {}
|
||||
|
||||
@ -124,6 +129,23 @@ public class DefaultGStringTranspiler implements GStringTranspiler {
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
@ -136,7 +158,10 @@ public class DefaultGStringTranspiler implements GStringTranspiler {
|
||||
return jStringBodyTextNode.getContent();
|
||||
} else if (node.hasExtension(GStringNodeExtension.class)) {
|
||||
final var gString = node.getExtension(GStringNodeExtension.class);
|
||||
return gString.getAsValidEmbeddableCode();
|
||||
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 "
|
||||
@ -165,7 +190,7 @@ public class DefaultGStringTranspiler implements GStringTranspiler {
|
||||
}
|
||||
case GStringScriptletExtension scriptlet -> {
|
||||
checkPrevBeforeDollar(prev, current).ifPresent(texts::add);
|
||||
// TODO
|
||||
values.add(this.handleScriptlet(scriptlet));
|
||||
checkNextAfterDollar(current, next).ifPresent(texts::add);
|
||||
}
|
||||
}
|
||||
|
@ -3,10 +3,13 @@ package groowt.view.web.transpile;
|
||||
import groowt.view.web.antlr.TokenList;
|
||||
import groowt.view.web.ast.node.BodyNode;
|
||||
import groowt.view.web.ast.node.CompilationUnitNode;
|
||||
import groowt.view.web.transpile.PreambleTranspiler.PreambleResult;
|
||||
import groowt.view.web.ast.node.PreambleNode;
|
||||
import groowt.view.web.transpile.util.GroovyUtil;
|
||||
import org.codehaus.groovy.ast.*;
|
||||
import org.codehaus.groovy.ast.expr.ClosureExpression;
|
||||
import org.codehaus.groovy.ast.expr.DeclarationExpression;
|
||||
import org.codehaus.groovy.ast.stmt.BlockStatement;
|
||||
import org.codehaus.groovy.ast.stmt.ExpressionStatement;
|
||||
import org.codehaus.groovy.ast.stmt.ReturnStatement;
|
||||
import org.codehaus.groovy.ast.stmt.Statement;
|
||||
import org.codehaus.groovy.control.CompilationUnit;
|
||||
@ -14,8 +17,11 @@ import org.codehaus.groovy.control.SourceUnit;
|
||||
import org.codehaus.groovy.control.io.ReaderSource;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static groowt.view.web.transpile.TranspilerUtil.*;
|
||||
@ -30,6 +36,8 @@ import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
|
||||
*/
|
||||
public class DefaultGroovyTranspiler implements GroovyTranspiler {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DefaultGroovyTranspiler.class);
|
||||
|
||||
private final CompilationUnit groovyCompilationUnit;
|
||||
private final String defaultPackageName;
|
||||
private final Supplier<? extends TranspilerConfiguration> configurationSupplier;
|
||||
@ -60,6 +68,96 @@ public class DefaultGroovyTranspiler implements GroovyTranspiler {
|
||||
}
|
||||
}
|
||||
|
||||
protected void checkPreambleClasses(String templateName, List<ClassNode> classNodes) {
|
||||
final ClassNode offending = classNodes.stream()
|
||||
.filter(classNode -> classNode.getName().equals(templateName))
|
||||
.findAny()
|
||||
.orElse(null);
|
||||
if (offending != null) {
|
||||
throw new IllegalArgumentException(
|
||||
templateName + " cannot define itself in the template. " +
|
||||
"Remove the class with that name."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected List<InnerClassNode> convertPreambleClassesToInnerClasses(ClassNode mainClassNode, List<ClassNode> classNodes) {
|
||||
final List<InnerClassNode> result = new ArrayList<>();
|
||||
for (final var classNode : classNodes) {
|
||||
if (classNode instanceof InnerClassNode innerClassNode) {
|
||||
result.add(innerClassNode);
|
||||
} else {
|
||||
final InnerClassNode icn = new InnerClassNode(
|
||||
mainClassNode,
|
||||
mainClassNode.getName() + "." + classNode.getNameWithoutPackage(),
|
||||
classNode.getModifiers(),
|
||||
classNode.getSuperClass(),
|
||||
classNode.getInterfaces(),
|
||||
classNode.getMixins()
|
||||
);
|
||||
icn.setDeclaringClass(mainClassNode);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected void handlePreamble(
|
||||
String templateName,
|
||||
String packageName,
|
||||
PreambleNode preambleNode,
|
||||
ClassNode mainClassNode,
|
||||
WebViewComponentModuleNode moduleNode
|
||||
) {
|
||||
final GroovyUtil.ConvertResult preambleConvert = GroovyUtil.convert(
|
||||
preambleNode.getGroovyCode().getAsValidGroovyCode()
|
||||
);
|
||||
|
||||
WebViewComponentModuleNode.copyTo(preambleConvert.moduleNode(), moduleNode);
|
||||
|
||||
final BlockStatement preambleBlock = preambleConvert.blockStatement();
|
||||
if (preambleBlock != null) {
|
||||
// Fields
|
||||
final List<Statement> preambleStatements = preambleBlock.getStatements();
|
||||
final List<DeclarationExpression> declarationsWithField = preambleStatements.stream()
|
||||
.filter(statement -> statement instanceof ExpressionStatement)
|
||||
.map(ExpressionStatement.class::cast)
|
||||
.map(ExpressionStatement::getExpression)
|
||||
.filter(expression -> expression instanceof DeclarationExpression)
|
||||
.map(DeclarationExpression.class::cast)
|
||||
.filter(declarationExpression ->
|
||||
!declarationExpression.getAnnotations(FIELD_ANNOTATION).isEmpty()
|
||||
)
|
||||
.toList();
|
||||
if (declarationsWithField.size() != preambleStatements.size()) {
|
||||
logger.warn(
|
||||
"{} contains script statements which are not supported. " +
|
||||
"Currently, only classes, methods, and field declarations (marked with @Field) " +
|
||||
"are supported. The rest will be ignored.",
|
||||
templateName
|
||||
);
|
||||
}
|
||||
declarationsWithField.forEach(declaration -> {
|
||||
declaration.setDeclaringClass(mainClassNode);
|
||||
});
|
||||
}
|
||||
|
||||
// move methods from script class
|
||||
final ClassNode scriptClass = preambleConvert.scriptClass();
|
||||
if (scriptClass != null) {
|
||||
scriptClass.getMethods().forEach(mainClassNode::addMethod);
|
||||
}
|
||||
|
||||
// handle classes
|
||||
final List<ClassNode> classNodes = preambleConvert.classNodes();
|
||||
this.checkPreambleClasses(templateName, classNodes);
|
||||
final List<ClassNode> toInner = classNodes.stream()
|
||||
.filter(classNode -> classNode != preambleConvert.scriptClass())
|
||||
.filter(classNode -> !classNode.isScript())
|
||||
.toList();
|
||||
final List<InnerClassNode> innerClassNodes = this.convertPreambleClassesToInnerClasses(mainClassNode, toInner);
|
||||
innerClassNodes.forEach(moduleNode::addClass);
|
||||
}
|
||||
|
||||
// Cases:
|
||||
// - no preamble -> create our own class
|
||||
// - some preamble, but no script -> create our own class but use imports/packageName from preamble
|
||||
@ -85,34 +183,26 @@ public class DefaultGroovyTranspiler implements GroovyTranspiler {
|
||||
final var moduleNode = new WebViewComponentModuleNode(sourceUnit);
|
||||
sourceUnit.setModuleNode(moduleNode);
|
||||
|
||||
ClassNode mainClassNode;
|
||||
final String packageName = this.getPackageName(moduleNode);
|
||||
moduleNode.setPackageName(packageName);
|
||||
|
||||
final PreambleResult preambleResult = configuration.getPreambleTranspiler().getPreambleResult(
|
||||
compilationUnitNode.getPreambleNode(),
|
||||
templateName,
|
||||
tokens
|
||||
final ClassNode mainClassNode = new ClassNode(
|
||||
packageName + "." + templateName,
|
||||
ACC_PUBLIC,
|
||||
ClassHelper.OBJECT_TYPE
|
||||
);
|
||||
if (preambleResult.moduleNode() != null) {
|
||||
WebViewComponentModuleNode.copyTo(preambleResult.moduleNode(), moduleNode);
|
||||
}
|
||||
if (preambleResult.scriptClass() != null) {
|
||||
mainClassNode = preambleResult.scriptClass();
|
||||
// do not add it to moduleNode because it's already there
|
||||
} else {
|
||||
final String packageName = this.getPackageName(moduleNode);
|
||||
final String templateClassName = packageName + "." + templateName;
|
||||
mainClassNode.setScript(true);
|
||||
mainClassNode.addInterface(TranspilerUtil.COMPONENT_TEMPLATE);
|
||||
|
||||
mainClassNode = new ClassNode(
|
||||
templateClassName,
|
||||
ACC_PUBLIC,
|
||||
ClassHelper.OBJECT_TYPE
|
||||
);
|
||||
mainClassNode.setScript(true);
|
||||
mainClassNode.addInterface(TranspilerUtil.COMPONENT_TEMPLATE);
|
||||
moduleNode.addClass(mainClassNode);
|
||||
|
||||
moduleNode.addClass(mainClassNode);
|
||||
// preamble
|
||||
final PreambleNode preambleNode = compilationUnitNode.getPreambleNode();
|
||||
if (preambleNode != null) {
|
||||
this.handlePreamble(templateName, packageName, preambleNode, mainClassNode, moduleNode);
|
||||
}
|
||||
|
||||
// renderer
|
||||
final var renderBlock = new BlockStatement();
|
||||
|
||||
final TranspilerState state = TranspilerState.withDefaultRootScope();
|
||||
@ -138,6 +228,8 @@ public class DefaultGroovyTranspiler implements GroovyTranspiler {
|
||||
},
|
||||
renderBlock
|
||||
);
|
||||
|
||||
// getRenderer()
|
||||
final Statement returnRendererStmt = new ReturnStatement(renderer);
|
||||
|
||||
final var voidClosure = ClassHelper.CLOSURE_TYPE.getPlainNodeReference();
|
||||
|
@ -1,32 +0,0 @@
|
||||
package groowt.view.web.transpile;
|
||||
|
||||
import groowt.view.web.antlr.TokenList;
|
||||
import groowt.view.web.ast.node.PreambleNode;
|
||||
import groowt.view.web.transpile.util.GroovyUtil;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DefaultPreambleTranspiler implements PreambleTranspiler {
|
||||
|
||||
@Override
|
||||
public PreambleResult getPreambleResult(
|
||||
@Nullable PreambleNode preambleNode,
|
||||
String templateName,
|
||||
TokenList tokens
|
||||
) {
|
||||
if (preambleNode == null) {
|
||||
return new PreambleResult(null, null, List.of());
|
||||
} else {
|
||||
final Token groovyToken = tokens.getGroovyToken(preambleNode.getGroovyCodeIndex());
|
||||
final GroovyUtil.ConvertResult convertResult = GroovyUtil.convert(groovyToken.getText(), templateName);
|
||||
return new PreambleResult(
|
||||
convertResult.moduleNode(),
|
||||
convertResult.scriptClass(),
|
||||
convertResult.classNodes()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -5,7 +5,6 @@ import jakarta.inject.Inject;
|
||||
public class DefaultTranspilerConfiguration implements TranspilerConfiguration {
|
||||
|
||||
private final OutStatementFactory outStatementFactory = new SimpleOutStatementFactory();
|
||||
private final PreambleTranspiler preambleTranspiler = new DefaultPreambleTranspiler();
|
||||
private final BodyTranspiler bodyTranspiler;
|
||||
|
||||
@Inject
|
||||
@ -20,11 +19,6 @@ public class DefaultTranspilerConfiguration implements TranspilerConfiguration {
|
||||
componentTranspiler.setValueNodeTranspiler(valueNodeTranspiler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreambleTranspiler getPreambleTranspiler() {
|
||||
return this.preambleTranspiler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BodyTranspiler getBodyTranspiler() {
|
||||
return this.bodyTranspiler;
|
||||
|
@ -1,20 +0,0 @@
|
||||
package groowt.view.web.transpile;
|
||||
|
||||
import groowt.view.web.antlr.TokenList;
|
||||
import groowt.view.web.ast.node.PreambleNode;
|
||||
import org.codehaus.groovy.ast.ClassNode;
|
||||
import org.codehaus.groovy.ast.ModuleNode;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface PreambleTranspiler {
|
||||
|
||||
record PreambleResult(
|
||||
@Nullable ModuleNode moduleNode, @Nullable ClassNode scriptClass,
|
||||
List<ClassNode> allClasses
|
||||
) {}
|
||||
|
||||
PreambleResult getPreambleResult(@Nullable PreambleNode preambleNode, String templateName, TokenList tokens);
|
||||
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
package groowt.view.web.transpile;
|
||||
|
||||
public interface TranspilerConfiguration {
|
||||
PreambleTranspiler getPreambleTranspiler();
|
||||
BodyTranspiler getBodyTranspiler();
|
||||
OutStatementFactory getOutStatementFactory();
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package groowt.view.web.transpile;
|
||||
|
||||
import groovy.lang.Tuple2;
|
||||
import groovy.transform.Field;
|
||||
import groowt.view.component.ComponentContext;
|
||||
import groowt.view.component.ComponentTemplate;
|
||||
import groowt.view.web.runtime.WebViewComponentWriter;
|
||||
@ -18,6 +19,7 @@ public final class TranspilerUtil {
|
||||
public static final ClassNode COMPONENT_TEMPLATE = ClassHelper.make(ComponentTemplate.class);
|
||||
public static final ClassNode OUT_TYPE = ClassHelper.make(WebViewComponentWriter.class);
|
||||
public static final ClassNode CONTEXT_CLASSNODE = ClassHelper.make(ComponentContext.class);
|
||||
public static final ClassNode FIELD_ANNOTATION = ClassHelper.make(Field.class);
|
||||
|
||||
public static final String GROOWT_VIEW_WEB = "groowt.view.web";
|
||||
public static final String OUT = "out";
|
||||
|
@ -14,16 +14,10 @@ import java.util.stream.Collectors;
|
||||
public class WebViewComponentModuleNode extends ModuleNode {
|
||||
|
||||
public static void copyTo(ModuleNode from, WebViewComponentModuleNode to) {
|
||||
to.setDescription(from.getDescription());
|
||||
to.setPackage(from.getPackage());
|
||||
to.setPackageName(from.getPackageName());
|
||||
to.setImportsResolved(from.hasImportsResolved());
|
||||
to.setMetaDataMap(from.getMetaDataMap());
|
||||
from.getImports().forEach(to::addImport);
|
||||
from.getStarImports().forEach(to::addStarImport);
|
||||
from.getStaticImports().forEach(to::addStaticImport);
|
||||
from.getStaticStarImports().forEach(to::addStaticStarImport);
|
||||
from.getClasses().forEach(to::addClass);
|
||||
}
|
||||
|
||||
protected final List<ImportNode> imports = new ArrayList<>();
|
||||
|
@ -1,15 +0,0 @@
|
||||
package groowt.view.web.transpiler;
|
||||
|
||||
import groowt.view.web.transpile.DefaultPreambleTranspiler;
|
||||
|
||||
public class DefaultPreambleTranspilerTests extends PreambleTranspilerTests {
|
||||
|
||||
protected static DefaultPreambleTranspiler getDefaultPreambleTranspiler() {
|
||||
return new DefaultPreambleTranspiler();
|
||||
}
|
||||
|
||||
public DefaultPreambleTranspilerTests() {
|
||||
super(getDefaultPreambleTranspiler());
|
||||
}
|
||||
|
||||
}
|
@ -4,12 +4,18 @@ import groowt.view.web.antlr.ParserUtil;
|
||||
import groowt.view.web.antlr.TokenList;
|
||||
import groowt.view.web.ast.DefaultAstBuilder;
|
||||
import groowt.view.web.ast.DefaultNodeFactory;
|
||||
import groowt.view.web.ast.node.BodyNode;
|
||||
import groowt.view.web.ast.node.CompilationUnitNode;
|
||||
import groowt.view.web.ast.node.GStringBodyTextNode;
|
||||
import groowt.view.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 {
|
||||
@ -23,20 +29,55 @@ public abstract class GStringTranspilerTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void gStringExpressionWithDollarReference() {
|
||||
final var source = "Hello, $target!";
|
||||
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 = (CompilationUnitNode) astBuilder.build(parseResult.getCompilationUnitContext());
|
||||
final var bodyNode = cuNode.getBodyNode();
|
||||
assertNotNull(bodyNode);
|
||||
final var gStringBodyTextNode = bodyNode.getAt(0, GStringBodyTextNode.class);
|
||||
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("Hello, $target!", gStringExpression.getText());
|
||||
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));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,17 +0,0 @@
|
||||
package groowt.view.web.transpiler;
|
||||
|
||||
import groowt.view.web.transpile.PreambleTranspiler;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public abstract class PreambleTranspilerTests {
|
||||
|
||||
protected final PreambleTranspiler preambleTranspiler;
|
||||
|
||||
public PreambleTranspilerTests(PreambleTranspiler preambleTranspiler) {
|
||||
this.preambleTranspiler = preambleTranspiler;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void smokeScreen() {}
|
||||
|
||||
}
|
@ -1,3 +1,3 @@
|
||||
#/usr/bin/env bash
|
||||
|
||||
java -cp build/libs/web-tools.jar:build/libs/web.jar $mainClassName "\$@"
|
||||
../gradlew toolsJar && java -cp build/libs/web-tools.jar:build/libs/web.jar $mainClassName "\$@"
|
||||
|
@ -36,6 +36,9 @@ class ConvertToGroovy implements Callable<Integer> {
|
||||
@CommandLine.Option(names = ['-o', '--out'], description = 'Write source files to disk instead of printing them.')
|
||||
boolean writeOut
|
||||
|
||||
@CommandLine.Option(names = ['-q', '--quiet'], description = 'Do not print the class source to the console.')
|
||||
boolean quiet
|
||||
|
||||
// Default is Phases.CLASS_GENERATION (7)
|
||||
@CommandLine.Option(
|
||||
names = ['-t', '--compilePhase'],
|
||||
@ -44,6 +47,12 @@ class ConvertToGroovy implements Callable<Integer> {
|
||||
)
|
||||
int compilePhase
|
||||
|
||||
@CommandLine.Option(
|
||||
names = ['-c', '--classes'],
|
||||
description = 'Whether to output the class files, if the compile phase is late enough. If this is false, any generated classes will be thrown out.'
|
||||
)
|
||||
boolean doClasses
|
||||
|
||||
@CommandLine.Option(
|
||||
names = ['-d', '--classesDir'],
|
||||
description = 'If the GroovyCompiler outputs classes, where to write them.'
|
||||
@ -60,7 +69,9 @@ class ConvertToGroovy implements Callable<Integer> {
|
||||
def astBuilder = new DefaultAstBuilder(new DefaultNodeFactory(tokenList))
|
||||
def cuNode = astBuilder.build(parseResult.compilationUnitContext) as CompilationUnitNode
|
||||
def config = new CompilerConfiguration().tap {
|
||||
it.targetDirectory = this.classesDir ?: new File(target.parentFile, 'classes')
|
||||
it.targetDirectory = this.doClasses
|
||||
? this.classesDir ?: new File(target.parentFile, 'classes')
|
||||
: File.createTempDir()
|
||||
}
|
||||
def gcu = new CompilationUnit(config)
|
||||
|
||||
@ -84,7 +95,8 @@ class ConvertToGroovy implements Callable<Integer> {
|
||||
if (this.writeOut) {
|
||||
def outFile = new File(target.parentFile, name + '.groovy')
|
||||
outFile.write(w.toString())
|
||||
} else {
|
||||
}
|
||||
if (!this.quiet) {
|
||||
println w.toString()
|
||||
}
|
||||
return true
|
||||
|
@ -0,0 +1,48 @@
|
||||
package groowt.view.web.tools
|
||||
|
||||
import groowt.view.component.DefaultComponentContext
|
||||
import groowt.view.web.DefaultWebComponentTemplateCompiler
|
||||
import groowt.view.web.DefaultWebViewComponent
|
||||
import groowt.view.web.WebViewTemplateComponentSource
|
||||
import groowt.view.web.runtime.WebViewComponentWriter
|
||||
import org.codehaus.groovy.control.CompilerConfiguration
|
||||
import picocli.CommandLine
|
||||
import picocli.CommandLine.Command
|
||||
import picocli.CommandLine.Option
|
||||
import picocli.CommandLine.Parameters
|
||||
|
||||
import java.util.concurrent.Callable
|
||||
|
||||
@Command(
|
||||
name = 'runTemplate',
|
||||
description = 'render a wvc template with the given params'
|
||||
)
|
||||
class RunTemplate implements Callable<Integer> {
|
||||
|
||||
@Parameters(arity = '1', description = 'The template file.')
|
||||
File template
|
||||
|
||||
@Option(
|
||||
names = ['-A', '--attr', '--attribute'],
|
||||
description = 'Attribute(s) to pass to the template.'
|
||||
)
|
||||
Map<String, String> properties
|
||||
|
||||
@Override
|
||||
Integer call() throws Exception {
|
||||
def component = new DefaultWebViewComponent(WebViewTemplateComponentSource.of(this.template))
|
||||
|
||||
def context = new DefaultComponentContext()
|
||||
context.pushDefaultScope()
|
||||
component.context = context
|
||||
|
||||
println component.render()
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
static void main(String[] args) {
|
||||
System.exit(new CommandLine(new RunTemplate()).execute(args))
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user