Groovyc can now accept .wvc files.

This commit is contained in:
JesseBrault0709 2024-05-17 08:54:36 +02:00
parent 9212d128c3
commit 63720693a3
8 changed files with 246 additions and 8 deletions

View File

@ -23,14 +23,14 @@ public class ComponentTemplateCompileException extends Exception {
@Override
public String getMessage() {
final var sb = new StringBuilder("Error in ").append(compileUnit.getSource().getDescription());
final @Nullable String position = this.getPosition();
final @Nullable String position = this.formatPosition();
if (position != null) {
sb.append(" at ").append(position);
}
return sb.append(": ").append(super.getMessage()).toString();
}
protected @Nullable String getPosition() {
protected @Nullable String formatPosition() {
return null;
}

View File

@ -133,6 +133,16 @@ tasks.register('toolsJar', Jar) {
dependsOn tasks.named('jar')
}
tasks.register('uberJar', Jar) {
group = 'build'
archiveBaseName = 'web-views-uber'
from sourceSets.main.output
from sourceSets.main.runtimeClasspath.filter(File.&exists).collect {
it.isDirectory() ? it : zipTree(it)
}
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
@NullCheck
class ToolSpec {

17
web-views/groovycTest Executable file
View File

@ -0,0 +1,17 @@
#!/usr/bin/env bash
if [ "$1" == "--debug" ]; then
gradle -q uberJar && \
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:8192 \
-cp build/libs/web-views-uber-0.1.0.jar \
org.codehaus.groovy.tools.FileSystemCompiler \
--configscript src/main/resources/groowt/view/web/groovyc/GroovycConfigurationScript.groovy \
-d groovyc-out \
sketching/helloTarget.wvc
else
gradle -q uberJar && \
groovyc -cp build/libs/web-views-uber-0.1.0.jar \
--configscript src/main/resources/groowt/view/web/groovyc/GroovycConfigurationScript.groovy \
-d groovyc-out \
sketching/helloTarget.wvc
fi

View File

@ -2,8 +2,8 @@ package groowt.view.web.compiler;
import groowt.view.component.compiler.ComponentTemplateCompileException;
import groowt.view.component.compiler.ComponentTemplateCompileUnit;
import groowt.view.web.antlr.TokenUtil;
import groowt.view.web.ast.node.Node;
import groowt.view.web.util.SourcePosition;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.jetbrains.annotations.Nullable;
@ -60,14 +60,25 @@ public class WebViewComponentTemplateCompileException extends ComponentTemplateC
this.node = node;
}
@Override
protected @Nullable String getPosition() {
public @Nullable SourcePosition getSourcePosition() {
if (this.node != null) {
return this.node.getTokenRange().getStartPosition().toStringLong();
return this.node.getTokenRange().getStartPosition();
} else if (this.parserRuleContext != null) {
return TokenUtil.formatTokenPosition(this.parserRuleContext.start);
final var start = this.parserRuleContext.start;
return new SourcePosition(start.getLine(), start.getCharPositionInLine() + 1);
} else if (this.terminalNode != null) {
return TokenUtil.formatTokenPosition(terminalNode.getSymbol());
final var symbol = terminalNode.getSymbol();
return new SourcePosition(symbol.getLine(), symbol.getCharPositionInLine() + 1);
} else {
return null;
}
}
@Override
protected @Nullable String formatPosition() {
final SourcePosition sourcePosition = this.getSourcePosition();
if (sourcePosition != null) {
return sourcePosition.toStringLong();
} else {
return null;
}

View File

@ -0,0 +1,184 @@
package groowt.view.web.groovyc;
import groowt.view.component.compiler.ComponentTemplateCompileException;
import groowt.view.component.compiler.DefaultComponentTemplateCompilerConfiguration;
import groowt.view.component.compiler.source.ComponentTemplateSource;
import groowt.view.web.analysis.MismatchedComponentTypeAnalysis;
import groowt.view.web.analysis.MismatchedComponentTypeError;
import groowt.view.web.antlr.*;
import groowt.view.web.ast.DefaultAstBuilder;
import groowt.view.web.ast.DefaultNodeFactory;
import groowt.view.web.ast.node.CompilationUnitNode;
import groowt.view.web.compiler.AnonymousWebViewComponent;
import groowt.view.web.compiler.MultipleWebViewComponentCompileErrorsException;
import groowt.view.web.compiler.WebViewComponentTemplateCompileException;
import groowt.view.web.compiler.WebViewComponentTemplateCompileUnit;
import groowt.view.web.transpile.DefaultGroovyTranspiler;
import groowt.view.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 java.io.IOException;
import java.io.Reader;
import java.util.List;
public class DelegatingWvcParserPlugin implements ParserPlugin {
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) {
final var actual = (WebViewComponentTemplateCompileException) e;
final SourcePosition sourcePosition = actual.getSourcePosition();
if (sourcePosition != null) {
return new ParserException(e.getMessage(), e, sourcePosition.line(), sourcePosition.column());
} else {
return new ParserException(e.getMessage(), e, -1, -1);
}
}
@Override
public ModuleNode buildAST(SourceUnit sourceUnit, ClassLoader classLoader, Reduction cst) throws ParserException {
final String sourceUnitName = sourceUnit.getName();
if (sourceUnitName.endsWith(".wvc")) {
final var compileUnit = new WebViewComponentTemplateCompileUnit(
AnonymousWebViewComponent.class,
ComponentTemplateSource.of(sourceUnit.getSource().getURI()),
"groowt.view.web.groovyc"
);
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();
throw this.translateException(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 this.translateException(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 var groovyTranspiler = new DefaultGroovyTranspiler();
try {
final SourceUnit transpiledSourceUnit = groovyTranspiler.transpile(
new DefaultComponentTemplateCompilerConfiguration(),
compileUnit,
cuNode,
sourceUnitName.substring(0, sourceUnitName.length() - 4)
);
return transpiledSourceUnit.getAST();
} catch (ComponentTemplateCompileException e) {
throw this.translateException(e);
}
} else {
return this.groovyParserPlugin.buildAST(sourceUnit, classLoader, cst);
}
}
}

View File

@ -0,0 +1,14 @@
package groowt.view.web.groovyc;
import org.apache.groovy.parser.antlr4.Antlr4ParserPlugin;
import org.codehaus.groovy.control.ParserPlugin;
import org.codehaus.groovy.control.ParserPluginFactory;
public class WvcParserPluginFactory extends ParserPluginFactory {
@Override
public ParserPlugin createParserPlugin() {
return new DelegatingWvcParserPlugin(new Antlr4ParserPlugin());
}
}

View File

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