CompilerPipeline refactor and creation of forwarding groovy/wvc compiler.
This commit is contained in:
parent
9bb76f819d
commit
13c84e8879
1
web-view-components-compiler/.gitignore
vendored
1
web-view-components-compiler/.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
bin
|
bin
|
||||||
groovyc-out
|
groovyc-out
|
||||||
|
groovy-wvc-out
|
||||||
|
@ -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')
|
||||||
|
@ -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() {}
|
||||||
|
|
||||||
|
}
|
@ -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();
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
package groowt.view.component.web.groovyc
|
|
||||||
|
|
||||||
configuration.pluginFactory = new WvcParserPluginFactory()
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -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()
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user