CompilerPipeline refactor and creation of forwarding groovy/wvc compiler.

This commit is contained in:
JesseBrault0709 2024-05-20 14:51:07 +02:00
parent 9bb76f819d
commit 13c84e8879
10 changed files with 359 additions and 318 deletions

View File

@ -1,2 +1,3 @@
bin bin
groovyc-out groovyc-out
groovy-wvc-out

View File

@ -148,6 +148,7 @@ def toolSpec = { String name, String mainClass ->
final List<ToolSpec> toolSpecs = [ final List<ToolSpec> toolSpecs = [
toolSpec('astFileMaker', 'AstFileMakerCli'), toolSpec('astFileMaker', 'AstFileMakerCli'),
toolSpec('convertToGroovy', 'ConvertToGroovy'), toolSpec('convertToGroovy', 'ConvertToGroovy'),
toolSpec('groovyWvc', 'GroovyWvcCompiler'),
toolSpec('lexer', 'LexerTool'), toolSpec('lexer', 'LexerTool'),
toolSpec('parser', 'ParserTool'), toolSpec('parser', 'ParserTool'),
toolSpec('parseTreeFileMaker', 'ParseTreeFileMakerCli') toolSpec('parseTreeFileMaker', 'ParseTreeFileMakerCli')

View File

@ -0,0 +1,124 @@
package groowt.view.component.web.compiler;
import groowt.view.component.web.analysis.MismatchedComponentTypeAnalysis;
import groowt.view.component.web.analysis.MismatchedComponentTypeError;
import groowt.view.component.web.antlr.*;
import groowt.view.component.web.ast.DefaultAstBuilder;
import groowt.view.component.web.ast.DefaultNodeFactory;
import groowt.view.component.web.ast.node.CompilationUnitNode;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.antlr.v4.runtime.tree.Tree;
import java.io.IOException;
import java.util.List;
public final class CompilerPipeline {
private static WebViewComponentTemplateCompileException getException(
WebViewComponentTemplateCompileUnit compileUnit,
TerminalNode terminalNode
) {
final Token offending = terminalNode.getSymbol();
final var exception = new WebViewComponentTemplateCompileException(
compileUnit, "Invalid token '" + TokenUtil.excerptToken(offending) + "'."
);
exception.setTerminalNode(terminalNode);
return exception;
}
private static WebViewComponentTemplateCompileException getException(
WebViewComponentTemplateCompileUnit compileUnit,
ParserRuleContext parserRuleContext
) {
final var exception = new WebViewComponentTemplateCompileException(
compileUnit,
"Parser error: " + parserRuleContext.exception.getMessage(),
parserRuleContext.exception
);
exception.setParserRuleContext(parserRuleContext);
return exception;
}
private static WebViewComponentTemplateCompileException getException(
WebViewComponentTemplateCompileUnit compileUnit,
Tree tree
) {
if (tree instanceof ParserRuleContext parserRuleContext) {
return getException(compileUnit, parserRuleContext);
} else if (tree instanceof TerminalNode terminalNode) {
return getException(compileUnit, terminalNode);
} else {
return new WebViewComponentTemplateCompileException(
compileUnit,
"Error at parser/lexer node " + tree.toString()
);
}
}
private static WebViewComponentTemplateCompileException getException(
WebViewComponentTemplateCompileUnit compileUnit,
MismatchedComponentTypeError error
) {
final var exception = new WebViewComponentTemplateCompileException(
compileUnit,
error.getMessage()
);
exception.setParserRuleContext(error.getComponent());
return exception;
}
public static CompilationUnitNode parseAndBuildAst(WebViewComponentTemplateCompileUnit compileUnit)
throws WebViewComponentTemplateCompileException, MultipleWebViewComponentCompileErrorsException {
final CompilationUnitParseResult parseResult;
try {
parseResult = ParserUtil.parseCompilationUnit(compileUnit.getGroovyReaderSource().getReader());
} catch (IOException e) {
throw new RuntimeException(e);
}
// check for parser/lexer errors
final var parseErrors = AntlrUtil.findErrorNodes(parseResult.getCompilationUnitContext());
if (!parseErrors.isEmpty()) {
if (parseErrors.getErrorCount() == 1) {
final var errorNode = parseErrors.getAll().getFirst();
throw getException(compileUnit, errorNode);
} else {
final var errorExceptions = parseErrors.getAll().stream()
.map(errorNode -> getException(compileUnit, errorNode))
.toList();
throw new MultipleWebViewComponentCompileErrorsException(
compileUnit,
errorExceptions
);
}
}
// check for mismatched type errors
final List<MismatchedComponentTypeError> mismatchedComponentTypeErrors =
MismatchedComponentTypeAnalysis.check(parseResult.getCompilationUnitContext());
if (!mismatchedComponentTypeErrors.isEmpty()) {
if (mismatchedComponentTypeErrors.size() == 1) {
throw new RuntimeException(getException(compileUnit, mismatchedComponentTypeErrors.getFirst()));
} else {
final var errorExceptions = mismatchedComponentTypeErrors.stream()
.map(error -> getException(compileUnit, error))
.toList();
throw new MultipleWebViewComponentCompileErrorsException(
compileUnit,
errorExceptions
);
}
}
// build ast
final var tokenList = new TokenList(parseResult.getTokenStream());
final var astBuilder = new DefaultAstBuilder(new DefaultNodeFactory(tokenList));
return (CompilationUnitNode) astBuilder.build(parseResult.getCompilationUnitContext());
}
private CompilerPipeline() {}
}

View File

@ -2,24 +2,14 @@ package groowt.view.component.web.compiler;
import groowt.view.component.compiler.*; import groowt.view.component.compiler.*;
import groowt.view.component.web.WebViewComponentBugError; import groowt.view.component.web.WebViewComponentBugError;
import groowt.view.component.web.analysis.MismatchedComponentTypeAnalysis;
import groowt.view.component.web.analysis.MismatchedComponentTypeError;
import groowt.view.component.web.antlr.*;
import groowt.view.component.web.ast.DefaultAstBuilder;
import groowt.view.component.web.ast.DefaultNodeFactory;
import groowt.view.component.web.ast.node.CompilationUnitNode; import groowt.view.component.web.ast.node.CompilationUnitNode;
import groowt.view.component.web.transpile.DefaultGroovyTranspiler; import groowt.view.component.web.transpile.DefaultGroovyTranspiler;
import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.antlr.v4.runtime.tree.Tree;
import org.codehaus.groovy.control.CompilationFailedException; import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.control.SourceUnit; import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.tools.GroovyClass; import org.codehaus.groovy.tools.GroovyClass;
import java.io.Reader;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
public class DefaultWebViewComponentTemplateCompiler public class DefaultWebViewComponentTemplateCompiler
@ -32,18 +22,6 @@ public class DefaultWebViewComponentTemplateCompiler
this.configuration = configuration; this.configuration = configuration;
} }
protected WebViewComponentTemplateCompileException getException(
WebViewComponentTemplateCompileUnit compileUnit,
TerminalNode terminalNode
) {
final Token offending = terminalNode.getSymbol();
final var exception = new WebViewComponentTemplateCompileException(
compileUnit, "Invalid token '" + TokenUtil.excerptToken(offending) + "'."
);
exception.setTerminalNode(terminalNode);
return exception;
}
protected WebViewComponentTemplateCompileException getException( protected WebViewComponentTemplateCompileException getException(
WebViewComponentTemplateCompileUnit compileUnit, WebViewComponentTemplateCompileUnit compileUnit,
ParserRuleContext parserRuleContext ParserRuleContext parserRuleContext
@ -57,80 +35,11 @@ public class DefaultWebViewComponentTemplateCompiler
return exception; return exception;
} }
protected WebViewComponentTemplateCompileException getException(
WebViewComponentTemplateCompileUnit compileUnit,
Tree tree
) {
if (tree instanceof ParserRuleContext parserRuleContext) {
return getException(compileUnit, parserRuleContext);
} else if (tree instanceof TerminalNode terminalNode) {
return getException(compileUnit, terminalNode);
} else {
return new WebViewComponentTemplateCompileException(
compileUnit,
"Error at parser/lexer node " + tree.toString()
);
}
}
protected WebViewComponentTemplateCompileException getException(
WebViewComponentTemplateCompileUnit compileUnit,
MismatchedComponentTypeError error
) {
final var exception = new WebViewComponentTemplateCompileException(
compileUnit,
error.getMessage()
);
exception.setParserRuleContext(error.getComponent());
return exception;
}
@Override @Override
protected ComponentTemplateCompileResult doCompile(WebViewComponentTemplateCompileUnit compileUnit) protected ComponentTemplateCompileResult doCompile(WebViewComponentTemplateCompileUnit compileUnit)
throws ComponentTemplateCompileException { throws ComponentTemplateCompileException {
final Reader sourceReader; final CompilationUnitNode cuNode = CompilerPipeline.parseAndBuildAst(compileUnit);
try {
sourceReader = compileUnit.getSource().toReader();
} catch (Exception e) {
throw new RuntimeException(e);
}
final CompilationUnitParseResult parseResult = ParserUtil.parseCompilationUnit(sourceReader);
// check for parser/lexer errors
final var parseErrors = AntlrUtil.findErrorNodes(parseResult.getCompilationUnitContext());
if (!parseErrors.isEmpty()) {
if (parseErrors.getErrorCount() == 1) {
final var errorNode = parseErrors.getAll().getFirst();
throw getException(compileUnit, errorNode);
} else {
final var errorExceptions = parseErrors.getAll().stream()
.map(errorNode -> getException(compileUnit, errorNode))
.toList();
throw new MultipleWebViewComponentCompileErrorsException(compileUnit, errorExceptions);
}
}
// check for mismatched type errors
final List<MismatchedComponentTypeError> mismatchedComponentTypeErrors =
MismatchedComponentTypeAnalysis.check(parseResult.getCompilationUnitContext());
if (!mismatchedComponentTypeErrors.isEmpty()) {
if (mismatchedComponentTypeErrors.size() == 1) {
throw getException(compileUnit, mismatchedComponentTypeErrors.getFirst());
} else {
final var errorExceptions = mismatchedComponentTypeErrors.stream()
.map(error -> getException(compileUnit, error))
.toList();
throw new MultipleWebViewComponentCompileErrorsException(compileUnit, errorExceptions);
}
}
// build ast
final var tokenList = new TokenList(parseResult.getTokenStream());
final var astBuilder = new DefaultAstBuilder(new DefaultNodeFactory(tokenList));
final var cuNode = (CompilationUnitNode) astBuilder.build(parseResult.getCompilationUnitContext());
// transpile to Groovy // transpile to Groovy
final var transpiler = new DefaultGroovyTranspiler(); final var transpiler = new DefaultGroovyTranspiler();

View File

@ -0,0 +1,109 @@
package groowt.view.component.web.groovyc;
import groowt.view.component.compiler.ComponentTemplateCompileException;
import groowt.view.component.compiler.DefaultComponentTemplateCompilerConfiguration;
import groowt.view.component.compiler.source.ComponentTemplateSource;
import groowt.view.component.web.WebViewComponentBugError;
import groowt.view.component.web.ast.node.CompilationUnitNode;
import groowt.view.component.web.compiler.*;
import groowt.view.component.web.transpile.DefaultGroovyTranspiler;
import groowt.view.component.web.util.SourcePosition;
import org.apache.groovy.parser.antlr4.Antlr4ParserPlugin;
import org.codehaus.groovy.ast.ModuleNode;
import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.control.ParserPlugin;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.syntax.ParserException;
import org.codehaus.groovy.syntax.Reduction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.Reader;
public class DelegatingWebViewComponentTemplateParserPlugin implements ParserPlugin {
private static final Logger logger = LoggerFactory.getLogger(DelegatingWebViewComponentTemplateParserPlugin.class);
private final Antlr4ParserPlugin groovyParserPlugin;
public DelegatingWebViewComponentTemplateParserPlugin(Antlr4ParserPlugin groovyParserPlugin) {
this.groovyParserPlugin = groovyParserPlugin;
}
@Override
public Reduction parseCST(SourceUnit sourceUnit, Reader reader) throws CompilationFailedException {
return this.groovyParserPlugin.parseCST(sourceUnit, reader); // returns null
}
protected ParserException translateException(ComponentTemplateCompileException e) {
if (e instanceof WebViewComponentTemplateCompileException single) {
final SourcePosition sourcePosition = single.getSourcePosition();
if (sourcePosition != null) {
return new ParserException(e.getMessage(), e, sourcePosition.line(), sourcePosition.column());
} else {
return new ParserException(e.getMessage(), e, 1, 1);
}
} else if (e instanceof MultipleWebViewComponentCompileErrorsException multiple) {
return new ParserException("There were multiple errors during compilation/transpilation.", multiple, 1, 1);
} else {
throw new WebViewComponentBugError(
"Cannot determine the type of non-WebViewComponent compile exception: "
+ e.getClass().getName()
);
}
}
protected void logException(ComponentTemplateCompileException e) {
logger.error(e.getMessage());
}
@Override
public ModuleNode buildAST(SourceUnit sourceUnit, ClassLoader classLoader, Reduction cst) throws ParserException {
final String sourceUnitFullName = sourceUnit.getName();
final int lastSlashIndex = sourceUnitFullName.lastIndexOf(File.separator);
final String sourceUnitFileName = sourceUnitFullName.substring(lastSlashIndex + 1);
if (sourceUnitFileName.endsWith(".wvc")) {
final var compileUnit = new DefaultWebViewComponentTemplateCompileUnit(
AnonymousWebViewComponent.class,
ComponentTemplateSource.of(sourceUnit.getSource().getURI()),
"" // default package
);
final CompilationUnitNode cuNode;
try {
cuNode = CompilerPipeline.parseAndBuildAst(compileUnit);
} catch (WebViewComponentTemplateCompileException | MultipleWebViewComponentCompileErrorsException e) {
this.logException(e);
throw this.translateException(e);
}
final var groovyTranspiler = new DefaultGroovyTranspiler();
final String nameWithoutExtension = sourceUnitFileName.substring(0, sourceUnitFileName.length() - 4);
try {
final SourceUnit transpiledSourceUnit = groovyTranspiler.transpile(
new DefaultComponentTemplateCompilerConfiguration(),
compileUnit,
cuNode,
nameWithoutExtension
);
return transpiledSourceUnit.getAST();
} catch (ComponentTemplateCompileException e) {
if (e instanceof WebViewComponentTemplateCompileException single) {
this.logException(single);
} else if (e instanceof MultipleWebViewComponentCompileErrorsException multiple) {
this.logException(multiple);
} else {
throw new WebViewComponentBugError(
"Could not determine type of non-WebViewComponent compile exception: "
+ e.getClass().getName()
);
}
throw this.translateException(e);
}
} else {
return this.groovyParserPlugin.buildAST(sourceUnit, classLoader, cst);
}
}
}

View File

@ -1,220 +0,0 @@
package groowt.view.component.web.groovyc;
import groowt.view.component.compiler.ComponentTemplateCompileException;
import groowt.view.component.compiler.DefaultComponentTemplateCompilerConfiguration;
import groowt.view.component.compiler.source.ComponentTemplateSource;
import groowt.view.component.web.WebViewComponentBugError;
import groowt.view.component.web.analysis.MismatchedComponentTypeAnalysis;
import groowt.view.component.web.analysis.MismatchedComponentTypeError;
import groowt.view.component.web.antlr.*;
import groowt.view.component.web.ast.DefaultAstBuilder;
import groowt.view.component.web.ast.DefaultNodeFactory;
import groowt.view.component.web.ast.node.CompilationUnitNode;
import groowt.view.component.web.compiler.*;
import groowt.view.component.web.transpile.DefaultGroovyTranspiler;
import groowt.view.component.web.util.SourcePosition;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.antlr.v4.runtime.tree.Tree;
import org.apache.groovy.parser.antlr4.Antlr4ParserPlugin;
import org.codehaus.groovy.ast.ModuleNode;
import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.control.ParserPlugin;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.syntax.ParserException;
import org.codehaus.groovy.syntax.Reduction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.util.List;
public class DelegatingWvcParserPlugin implements ParserPlugin {
private static final Logger logger = LoggerFactory.getLogger(DelegatingWvcParserPlugin.class);
private final Antlr4ParserPlugin groovyParserPlugin;
public DelegatingWvcParserPlugin(Antlr4ParserPlugin groovyParserPlugin) {
this.groovyParserPlugin = groovyParserPlugin;
}
@Override
public Reduction parseCST(SourceUnit sourceUnit, Reader reader) throws CompilationFailedException {
return this.groovyParserPlugin.parseCST(sourceUnit, reader); // returns null
}
protected WebViewComponentTemplateCompileException getException(
WebViewComponentTemplateCompileUnit compileUnit,
TerminalNode terminalNode
) {
final Token offending = terminalNode.getSymbol();
final var exception = new WebViewComponentTemplateCompileException(
compileUnit, "Invalid token '" + TokenUtil.excerptToken(offending) + "'."
);
exception.setTerminalNode(terminalNode);
return exception;
}
protected WebViewComponentTemplateCompileException getException(
WebViewComponentTemplateCompileUnit compileUnit,
ParserRuleContext parserRuleContext
) {
final var exception = new WebViewComponentTemplateCompileException(
compileUnit,
"Parser error: " + parserRuleContext.exception.getMessage(),
parserRuleContext.exception
);
exception.setParserRuleContext(parserRuleContext);
return exception;
}
protected WebViewComponentTemplateCompileException getException(
WebViewComponentTemplateCompileUnit compileUnit,
Tree tree
) {
if (tree instanceof ParserRuleContext parserRuleContext) {
return getException(compileUnit, parserRuleContext);
} else if (tree instanceof TerminalNode terminalNode) {
return getException(compileUnit, terminalNode);
} else {
return new WebViewComponentTemplateCompileException(
compileUnit,
"Error at parser/lexer node " + tree.toString()
);
}
}
protected WebViewComponentTemplateCompileException getException(
WebViewComponentTemplateCompileUnit compileUnit,
MismatchedComponentTypeError error
) {
final var exception = new WebViewComponentTemplateCompileException(
compileUnit,
error.getMessage()
);
exception.setParserRuleContext(error.getComponent());
return exception;
}
protected ParserException translateException(ComponentTemplateCompileException e) {
if (e instanceof WebViewComponentTemplateCompileException single) {
final SourcePosition sourcePosition = single.getSourcePosition();
if (sourcePosition != null) {
return new ParserException(e.getMessage(), e, sourcePosition.line(), sourcePosition.column());
} else {
return new ParserException(e.getMessage(), e, 1, 1);
}
} else if (e instanceof MultipleWebViewComponentCompileErrorsException multiple) {
return new ParserException("There were multiple errors during compilation/transpilation.", multiple, 1, 1);
} else {
throw new WebViewComponentBugError(
"Cannot determine the type of non-WebViewComponent compile exception: "
+ e.getClass().getName()
);
}
}
protected void logException(WebViewComponentTemplateCompileException e) {
logger.error(e.getMessage());
}
protected void logException(MultipleWebViewComponentCompileErrorsException e) {
logger.error(e.getMessage());
}
@Override
public ModuleNode buildAST(SourceUnit sourceUnit, ClassLoader classLoader, Reduction cst) throws ParserException {
final String sourceUnitFullName = sourceUnit.getName();
final int lastSlashIndex = sourceUnitFullName.lastIndexOf(File.separator);
final String sourceUnitFileName = sourceUnitFullName.substring(lastSlashIndex + 1);
if (sourceUnitFileName.endsWith(".wvc")) {
final var compileUnit = new DefaultWebViewComponentTemplateCompileUnit(
AnonymousWebViewComponent.class,
ComponentTemplateSource.of(sourceUnit.getSource().getURI()),
"" // default package
);
final CompilationUnitParseResult parseResult;
try {
parseResult = ParserUtil.parseCompilationUnit(sourceUnit.getSource().getReader());
} catch (IOException e) {
throw new RuntimeException(e);
}
// check for parser/lexer errors
final var parseErrors = AntlrUtil.findErrorNodes(parseResult.getCompilationUnitContext());
if (!parseErrors.isEmpty()) {
if (parseErrors.getErrorCount() == 1) {
final var errorNode = parseErrors.getAll().getFirst();
throw this.translateException(getException(compileUnit, errorNode));
} else {
final var errorExceptions = parseErrors.getAll().stream()
.map(errorNode -> getException(compileUnit, errorNode))
.toList();
final var multiple = new MultipleWebViewComponentCompileErrorsException(
compileUnit,
errorExceptions
);
this.logException(multiple);
throw this.translateException(multiple);
}
}
// check for mismatched type errors
final List<MismatchedComponentTypeError> mismatchedComponentTypeErrors =
MismatchedComponentTypeAnalysis.check(parseResult.getCompilationUnitContext());
if (!mismatchedComponentTypeErrors.isEmpty()) {
if (mismatchedComponentTypeErrors.size() == 1) {
throw new RuntimeException(getException(compileUnit, mismatchedComponentTypeErrors.getFirst()));
} else {
final var errorExceptions = mismatchedComponentTypeErrors.stream()
.map(error -> getException(compileUnit, error))
.toList();
final var multiple = new MultipleWebViewComponentCompileErrorsException(
compileUnit,
errorExceptions
);
this.logException(multiple);
throw this.translateException(multiple);
}
}
// build ast
final var tokenList = new TokenList(parseResult.getTokenStream());
final var astBuilder = new DefaultAstBuilder(new DefaultNodeFactory(tokenList));
final var cuNode = (CompilationUnitNode) astBuilder.build(parseResult.getCompilationUnitContext());
final var groovyTranspiler = new DefaultGroovyTranspiler();
final String nameWithoutExtension = sourceUnitFileName.substring(0, sourceUnitFileName.length() - 4);
try {
final SourceUnit transpiledSourceUnit = groovyTranspiler.transpile(
new DefaultComponentTemplateCompilerConfiguration(),
compileUnit,
cuNode,
nameWithoutExtension
);
return transpiledSourceUnit.getAST();
} catch (ComponentTemplateCompileException e) {
if (e instanceof WebViewComponentTemplateCompileException single) {
this.logException(single);
} else if (e instanceof MultipleWebViewComponentCompileErrorsException multiple) {
this.logException(multiple);
} else {
throw new WebViewComponentBugError(
"Could not determine type of non-WebViewComponent compile exception: "
+ e.getClass().getName()
);
}
throw this.translateException(e);
}
} else {
return this.groovyParserPlugin.buildAST(sourceUnit, classLoader, cst);
}
}
}

View File

@ -1,3 +0,0 @@
package groowt.view.component.web.groovyc
configuration.pluginFactory = new WvcParserPluginFactory()

View File

@ -4,11 +4,11 @@ import org.apache.groovy.parser.antlr4.Antlr4ParserPlugin;
import org.codehaus.groovy.control.ParserPlugin; import org.codehaus.groovy.control.ParserPlugin;
import org.codehaus.groovy.control.ParserPluginFactory; import org.codehaus.groovy.control.ParserPluginFactory;
public class WvcParserPluginFactory extends ParserPluginFactory { public class WebViewComponentParserPluginFactory extends ParserPluginFactory {
@Override @Override
public ParserPlugin createParserPlugin() { public ParserPlugin createParserPlugin() {
return new DelegatingWvcParserPlugin(new Antlr4ParserPlugin()); return new DelegatingWebViewComponentTemplateParserPlugin(new Antlr4ParserPlugin());
} }
} }

View File

@ -2,4 +2,4 @@ package groowt.view.component.web.groovyc
import org.codehaus.groovy.control.CompilerConfiguration import org.codehaus.groovy.control.CompilerConfiguration
(configuration as CompilerConfiguration).pluginFactory = new WvcParserPluginFactory() (configuration as CompilerConfiguration).pluginFactory = new WebViewComponentParserPluginFactory()

View File

@ -0,0 +1,120 @@
package groowt.view.component.web.tools;
import groowt.view.component.compiler.ComponentTemplateCompileException;
import groowt.view.component.web.groovyc.WebViewComponentParserPluginFactory;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.control.CompilationUnit;
import org.codehaus.groovy.control.CompilerConfiguration;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
@Command(
name = "groovyWvc",
description = "Compile Groovy, Java, and WebViewComponent (.wvc) files together.",
mixinStandardHelpOptions = true
)
public final class GroovyWvcCompiler implements Callable<Integer> {
private static final Logger logger = LogManager.getLogger(GroovyWvcCompiler.class);
@CommandLine.Parameters(
arity = "1..*",
description = "The files to compile, each ending with .groovy, .java, or .wvc."
)
private List<File> files;
@CommandLine.Option(
names = { "-d", "--out-dir" },
description = "The output dir for classes.",
defaultValue = "groovy-wvc-out"
)
private File outDir;
@CommandLine.Option(
names = { "-j", "--joint" },
description = "Whether to joint-compile java files as well."
)
private boolean jointCompile;
@CommandLine.Option(
names = { "-L", "--log-level" },
description = "The desired logging level.",
defaultValue = "WARNING"
)
private String logLevel;
public static void main(String[] args) {
System.exit(new CommandLine(new GroovyWvcCompiler()).execute(args));
}
@Override
public Integer call() {
final Level logLevel = Level.toLevel(this.logLevel);
final LoggerContext context = (LoggerContext) LogManager.getContext(false);
context.getRootLogger().setLevel(logLevel);
return this.doCompile();
}
public Integer doCompile() {
final CompilerConfiguration configuration = new CompilerConfiguration();
configuration.setPluginFactory(new WebViewComponentParserPluginFactory());
final CompilationUnit compilationUnit = new CompilationUnit(configuration);
configuration.setTargetDirectory(this.outDir);
this.files.forEach(compilationUnit::addSource);
this.files.forEach(file -> {
if (file.isFile()) {
try {
final URL url = file.getAbsoluteFile().getParentFile().toURI().toURL();
compilationUnit.getClassLoader().addURL(url);
} catch (MalformedURLException e) {
logger.error("Could not create URL for file: {}", file);
}
}
});
if (this.jointCompile) {
final File stubDir;
try {
stubDir = Files.createTempDirectory("groovy-wvc-generated-java-source-").toFile();
} catch (IOException e) {
logger.error("Could not create java stubs dir.");
return 1;
}
final Map<String, Object> jointCompilationOptions = new HashMap<>();
jointCompilationOptions.put("stubDir", stubDir);
configuration.setJointCompilationOptions(jointCompilationOptions);
}
try {
compilationUnit.compile();
} catch (Exception e) {
if (e instanceof CompilationFailedException compilationFailedException) {
final Throwable cause = compilationFailedException.getCause();
if (cause instanceof ComponentTemplateCompileException componentTemplateCompileException) {
logger.error(componentTemplateCompileException);
return 1;
}
}
logger.error(e);
return 1;
}
return 0;
}
}