From 13c84e8879f0737c908b6caca34971d05befa7ca Mon Sep 17 00:00:00 2001 From: JesseBrault0709 <62299747+JesseBrault0709@users.noreply.github.com> Date: Mon, 20 May 2024 14:51:07 +0200 Subject: [PATCH] CompilerPipeline refactor and creation of forwarding groovy/wvc compiler. --- web-view-components-compiler/.gitignore | 1 + web-view-components-compiler/build.gradle | 1 + .../web/compiler/CompilerPipeline.java | 124 ++++++++++ ...faultWebViewComponentTemplateCompiler.java | 93 +------- ...gWebViewComponentTemplateParserPlugin.java | 109 +++++++++ .../groovyc/DelegatingWvcParserPlugin.java | 220 ------------------ .../groovyc/GroovycConfigurationScript.groovy | 3 - ... WebViewComponentParserPluginFactory.java} | 4 +- .../groovyc/groovycConfigurationScript.groovy | 2 +- .../web/tools/GroovyWvcCompiler.java | 120 ++++++++++ 10 files changed, 359 insertions(+), 318 deletions(-) create mode 100644 web-view-components-compiler/src/main/java/groowt/view/component/web/compiler/CompilerPipeline.java create mode 100644 web-view-components-compiler/src/main/java/groowt/view/component/web/groovyc/DelegatingWebViewComponentTemplateParserPlugin.java delete mode 100644 web-view-components-compiler/src/main/java/groowt/view/component/web/groovyc/DelegatingWvcParserPlugin.java delete mode 100644 web-view-components-compiler/src/main/java/groowt/view/component/web/groovyc/GroovycConfigurationScript.groovy rename web-view-components-compiler/src/main/java/groowt/view/component/web/groovyc/{WvcParserPluginFactory.java => WebViewComponentParserPluginFactory.java} (61%) create mode 100644 web-view-components-compiler/src/tools/java/groowt/view/component/web/tools/GroovyWvcCompiler.java diff --git a/web-view-components-compiler/.gitignore b/web-view-components-compiler/.gitignore index 30b167a..6369cca 100644 --- a/web-view-components-compiler/.gitignore +++ b/web-view-components-compiler/.gitignore @@ -1,2 +1,3 @@ bin groovyc-out +groovy-wvc-out diff --git a/web-view-components-compiler/build.gradle b/web-view-components-compiler/build.gradle index 1c1867c..7609c62 100644 --- a/web-view-components-compiler/build.gradle +++ b/web-view-components-compiler/build.gradle @@ -148,6 +148,7 @@ def toolSpec = { String name, String mainClass -> final List toolSpecs = [ toolSpec('astFileMaker', 'AstFileMakerCli'), toolSpec('convertToGroovy', 'ConvertToGroovy'), + toolSpec('groovyWvc', 'GroovyWvcCompiler'), toolSpec('lexer', 'LexerTool'), toolSpec('parser', 'ParserTool'), toolSpec('parseTreeFileMaker', 'ParseTreeFileMakerCli') diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/compiler/CompilerPipeline.java b/web-view-components-compiler/src/main/java/groowt/view/component/web/compiler/CompilerPipeline.java new file mode 100644 index 0000000..c0df674 --- /dev/null +++ b/web-view-components-compiler/src/main/java/groowt/view/component/web/compiler/CompilerPipeline.java @@ -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 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() {} + +} diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/compiler/DefaultWebViewComponentTemplateCompiler.java b/web-view-components-compiler/src/main/java/groowt/view/component/web/compiler/DefaultWebViewComponentTemplateCompiler.java index c60aed6..0b7fd76 100644 --- a/web-view-components-compiler/src/main/java/groowt/view/component/web/compiler/DefaultWebViewComponentTemplateCompiler.java +++ b/web-view-components-compiler/src/main/java/groowt/view/component/web/compiler/DefaultWebViewComponentTemplateCompiler.java @@ -2,24 +2,14 @@ package groowt.view.component.web.compiler; import groowt.view.component.compiler.*; 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.transpile.DefaultGroovyTranspiler; 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.SourceUnit; import org.codehaus.groovy.tools.GroovyClass; -import java.io.Reader; import java.util.HashSet; -import java.util.List; import java.util.Set; public class DefaultWebViewComponentTemplateCompiler @@ -32,18 +22,6 @@ public class DefaultWebViewComponentTemplateCompiler 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( WebViewComponentTemplateCompileUnit compileUnit, ParserRuleContext parserRuleContext @@ -57,80 +35,11 @@ public class DefaultWebViewComponentTemplateCompiler 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 protected ComponentTemplateCompileResult doCompile(WebViewComponentTemplateCompileUnit compileUnit) throws ComponentTemplateCompileException { - final Reader sourceReader; - 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 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()); + final CompilationUnitNode cuNode = CompilerPipeline.parseAndBuildAst(compileUnit); // transpile to Groovy final var transpiler = new DefaultGroovyTranspiler(); diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/groovyc/DelegatingWebViewComponentTemplateParserPlugin.java b/web-view-components-compiler/src/main/java/groowt/view/component/web/groovyc/DelegatingWebViewComponentTemplateParserPlugin.java new file mode 100644 index 0000000..f400f6d --- /dev/null +++ b/web-view-components-compiler/src/main/java/groowt/view/component/web/groovyc/DelegatingWebViewComponentTemplateParserPlugin.java @@ -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); + } + } + +} diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/groovyc/DelegatingWvcParserPlugin.java b/web-view-components-compiler/src/main/java/groowt/view/component/web/groovyc/DelegatingWvcParserPlugin.java deleted file mode 100644 index 1817198..0000000 --- a/web-view-components-compiler/src/main/java/groowt/view/component/web/groovyc/DelegatingWvcParserPlugin.java +++ /dev/null @@ -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 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); - } - } - -} diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/groovyc/GroovycConfigurationScript.groovy b/web-view-components-compiler/src/main/java/groowt/view/component/web/groovyc/GroovycConfigurationScript.groovy deleted file mode 100644 index 650b24a..0000000 --- a/web-view-components-compiler/src/main/java/groowt/view/component/web/groovyc/GroovycConfigurationScript.groovy +++ /dev/null @@ -1,3 +0,0 @@ -package groowt.view.component.web.groovyc - -configuration.pluginFactory = new WvcParserPluginFactory() diff --git a/web-view-components-compiler/src/main/java/groowt/view/component/web/groovyc/WvcParserPluginFactory.java b/web-view-components-compiler/src/main/java/groowt/view/component/web/groovyc/WebViewComponentParserPluginFactory.java similarity index 61% rename from web-view-components-compiler/src/main/java/groowt/view/component/web/groovyc/WvcParserPluginFactory.java rename to web-view-components-compiler/src/main/java/groowt/view/component/web/groovyc/WebViewComponentParserPluginFactory.java index 686c7d8..58da6fa 100644 --- a/web-view-components-compiler/src/main/java/groowt/view/component/web/groovyc/WvcParserPluginFactory.java +++ b/web-view-components-compiler/src/main/java/groowt/view/component/web/groovyc/WebViewComponentParserPluginFactory.java @@ -4,11 +4,11 @@ import org.apache.groovy.parser.antlr4.Antlr4ParserPlugin; import org.codehaus.groovy.control.ParserPlugin; import org.codehaus.groovy.control.ParserPluginFactory; -public class WvcParserPluginFactory extends ParserPluginFactory { +public class WebViewComponentParserPluginFactory extends ParserPluginFactory { @Override public ParserPlugin createParserPlugin() { - return new DelegatingWvcParserPlugin(new Antlr4ParserPlugin()); + return new DelegatingWebViewComponentTemplateParserPlugin(new Antlr4ParserPlugin()); } } diff --git a/web-view-components-compiler/src/main/resources/groowt/view/component/web/groovyc/groovycConfigurationScript.groovy b/web-view-components-compiler/src/main/resources/groowt/view/component/web/groovyc/groovycConfigurationScript.groovy index c57226f..5521402 100644 --- a/web-view-components-compiler/src/main/resources/groowt/view/component/web/groovyc/groovycConfigurationScript.groovy +++ b/web-view-components-compiler/src/main/resources/groowt/view/component/web/groovyc/groovycConfigurationScript.groovy @@ -2,4 +2,4 @@ package groowt.view.component.web.groovyc import org.codehaus.groovy.control.CompilerConfiguration -(configuration as CompilerConfiguration).pluginFactory = new WvcParserPluginFactory() +(configuration as CompilerConfiguration).pluginFactory = new WebViewComponentParserPluginFactory() diff --git a/web-view-components-compiler/src/tools/java/groowt/view/component/web/tools/GroovyWvcCompiler.java b/web-view-components-compiler/src/tools/java/groowt/view/component/web/tools/GroovyWvcCompiler.java new file mode 100644 index 0000000..43b7fe7 --- /dev/null +++ b/web-view-components-compiler/src/tools/java/groowt/view/component/web/tools/GroovyWvcCompiler.java @@ -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 { + + 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 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 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; + } + +}