WIP visitor transformer
This commit is contained in:
parent
cfce3e4dbb
commit
d4b6714229
15 changed files with 200 additions and 20 deletions
|
@ -17,7 +17,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>nu.zoom.dsl</groupId>
|
<groupId>nu.zoom.dsl</groupId>
|
||||||
<artifactId>parser</artifactId>
|
<artifactId>parser</artifactId>
|
||||||
<version>${parent.version}</version>
|
<version>${project.parent.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,11 @@
|
||||||
<artifactId>antlr4-runtime</artifactId>
|
<artifactId>antlr4-runtime</artifactId>
|
||||||
<version>4.13.0</version>
|
<version>4.13.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>info.picocli</groupId>
|
||||||
|
<artifactId>picocli</artifactId>
|
||||||
|
<version>4.7.6</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
|
@ -42,6 +47,9 @@
|
||||||
<groupId>org.antlr</groupId>
|
<groupId>org.antlr</groupId>
|
||||||
<artifactId>antlr4-maven-plugin</artifactId>
|
<artifactId>antlr4-maven-plugin</artifactId>
|
||||||
<version>4.13.1</version>
|
<version>4.13.1</version>
|
||||||
|
<configuration>
|
||||||
|
<visitor>true</visitor>
|
||||||
|
</configuration>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<goals>
|
<goals>
|
||||||
|
|
|
@ -1,23 +1,24 @@
|
||||||
grammar Endpoints;
|
grammar Endpoints;
|
||||||
|
document : generatorconfig? (compoundType|endpoint)* ;
|
||||||
generatorconfig : '{' (configitem)? (',' configitem)* '}';
|
generatorconfig : '{' (configitem)? (',' configitem)* '}';
|
||||||
configitem : configkey ':' configvalue ;
|
configitem : configkey ':' configvalue ;
|
||||||
configkey : IDENTIFIER ;
|
configkey : IDENTIFIER ;
|
||||||
configvalue : (IDENTIFIER|VALUE) ;
|
configvalue : (IDENTIFIER|VALUE) ;
|
||||||
|
compoundType : compoundTypeName compoundFields ;
|
||||||
|
compoundTypeName : IDENTIFIER ;
|
||||||
|
compoundFields : '(' compoundField (',' compoundField)* ')' ;
|
||||||
|
compoundField : fieldName ':' fieldType ;
|
||||||
fieldName : IDENTIFIER ;
|
fieldName : IDENTIFIER ;
|
||||||
fieldType : IDENTIFIER ;
|
fieldType : IDENTIFIER ;
|
||||||
compoundTypeName : IDENTIFIER ;
|
endpoint : path '<' (compoundType | IDENTIFIER) ;
|
||||||
compoundField : fieldName ':' fieldType ;
|
|
||||||
compoundFields : '(' compoundField (',' compoundField)* ')' ;
|
|
||||||
compoundType : compoundTypeName compoundFields ;
|
|
||||||
pathSegment : '/' (IDENTIFIER|VALUE) ;
|
|
||||||
path : (pathSegment)+ ;
|
path : (pathSegment)+ ;
|
||||||
endpoint : path '<' (compoundFields | IDENTIFIER) ;
|
pathSegment : '/' (IDENTIFIER|VALUE) ;
|
||||||
|
|
||||||
|
|
||||||
fragment DIGIT : [0-9] ;
|
fragment DIGIT : [0-9] ;
|
||||||
fragment LOWERCASE : [a-z] ;
|
fragment LOWERCASE : [a-z] ;
|
||||||
fragment UPPERCASE : [A-Z] ;
|
fragment UPPERCASE : [A-Z] ;
|
||||||
|
COMMENT : '/*' (IDENTIFIER|VALUE)* '*/' -> skip ;
|
||||||
WS : [ \t\n\r]+ -> skip;
|
WS : [ \t\n\r]+ -> skip;
|
||||||
IDENTIFIER : (LOWERCASE | UPPERCASE) (LOWERCASE | UPPERCASE | DIGIT)* ;
|
IDENTIFIER : (LOWERCASE | UPPERCASE) (LOWERCASE | UPPERCASE | DIGIT)* ;
|
||||||
VALUE : ~[ ,{}:()\n\t\r/<>"']+ ;
|
VALUE : ~[ ,{}:()\n\t\r/<>"#';*]+ ;
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
package nu.zoom.dsl;
|
|
||||||
|
|
||||||
public class Main {
|
|
||||||
public static void main(String[] args) {
|
|
||||||
System.out.println("Hello, World!");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
package nu.zoom.dsl.ast;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public record CompoundTypeNode(String name, List<FieldNode> fields) {
|
||||||
|
}
|
4
parser/src/main/java/nu/zoom/dsl/ast/ConfigItemNode.java
Normal file
4
parser/src/main/java/nu/zoom/dsl/ast/ConfigItemNode.java
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
package nu.zoom.dsl.ast;
|
||||||
|
|
||||||
|
public record ConfigItemNode(String key, String value) {
|
||||||
|
}
|
9
parser/src/main/java/nu/zoom/dsl/ast/DocumentNode.java
Normal file
9
parser/src/main/java/nu/zoom/dsl/ast/DocumentNode.java
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package nu.zoom.dsl.ast;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public record DocumentNode(
|
||||||
|
List<ConfigItemNode> configItems,
|
||||||
|
List<CompoundTypeNode> typeDefinitions,
|
||||||
|
List<EndpointNode> endpoints) {
|
||||||
|
}
|
8
parser/src/main/java/nu/zoom/dsl/ast/EndpointNode.java
Normal file
8
parser/src/main/java/nu/zoom/dsl/ast/EndpointNode.java
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
package nu.zoom.dsl.ast;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public record EndpointNode(
|
||||||
|
PathsNode paths,
|
||||||
|
String inputType) {
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package nu.zoom.dsl.ast;
|
||||||
|
|
||||||
|
import nu.zoom.dsl.parser.EndpointsBaseVisitor;
|
||||||
|
import nu.zoom.dsl.parser.EndpointsParser;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class EndpointsVisitorTransformer extends EndpointsBaseVisitor<EndpointsParser.DocumentContext> {
|
||||||
|
private ArrayList<EndpointNode> endpoints = new ArrayList<>();
|
||||||
|
private ArrayList<ConfigItemNode> config = new ArrayList<>();
|
||||||
|
private ArrayList<CompoundTypeNode> dataTypes = new ArrayList<>();
|
||||||
|
public EndpointsVisitorTransformer() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EndpointsParser.DocumentContext visitConfigitem(EndpointsParser.ConfigitemContext ctx) {
|
||||||
|
String configKey = ctx.configkey().IDENTIFIER().getText() ;
|
||||||
|
String configValue = getText(ctx.configvalue()) ;
|
||||||
|
this.config.add(new ConfigItemNode(configKey, configValue));
|
||||||
|
return super.visitConfigitem(ctx) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EndpointsParser.DocumentContext visitCompoundType(EndpointsParser.CompoundTypeContext ctx) {
|
||||||
|
return super.visitCompoundType(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EndpointsParser.DocumentContext visitEndpoint(EndpointsParser.EndpointContext ctx) {
|
||||||
|
return super.visitEndpoint(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getText(EndpointsParser.ConfigvalueContext ctx) {
|
||||||
|
String identifierText = (ctx.IDENTIFIER() != null) ? ctx.IDENTIFIER().getText() : "" ;
|
||||||
|
String valueText = (ctx.VALUE() != null) ? ctx.VALUE().getText() : "" ;
|
||||||
|
return identifierText + valueText;
|
||||||
|
}
|
||||||
|
}
|
4
parser/src/main/java/nu/zoom/dsl/ast/FieldNode.java
Normal file
4
parser/src/main/java/nu/zoom/dsl/ast/FieldNode.java
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
package nu.zoom.dsl.ast;
|
||||||
|
|
||||||
|
public record FieldNode(String name, String type) {
|
||||||
|
}
|
22
parser/src/main/java/nu/zoom/dsl/ast/ParserWrapper.java
Normal file
22
parser/src/main/java/nu/zoom/dsl/ast/ParserWrapper.java
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package nu.zoom.dsl.ast;
|
||||||
|
|
||||||
|
import nu.zoom.dsl.parser.EndpointsLexer;
|
||||||
|
import nu.zoom.dsl.parser.EndpointsParser;
|
||||||
|
import org.antlr.v4.runtime.CharStreams;
|
||||||
|
import org.antlr.v4.runtime.CommonTokenStream;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
public class ParserWrapper {
|
||||||
|
public DocumentNode parse(Path sourcePath) throws IOException {
|
||||||
|
var ins = CharStreams.fromPath(sourcePath, StandardCharsets.UTF_8);
|
||||||
|
EndpointsLexer lexer = new EndpointsLexer(ins);
|
||||||
|
EndpointsParser parser = new EndpointsParser(new CommonTokenStream(lexer));
|
||||||
|
var document= parser.document() ;
|
||||||
|
new EndpointsVisitorTransformer().visit(document);
|
||||||
|
return null ;
|
||||||
|
}
|
||||||
|
}
|
6
parser/src/main/java/nu/zoom/dsl/ast/PathsNode.java
Normal file
6
parser/src/main/java/nu/zoom/dsl/ast/PathsNode.java
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
package nu.zoom.dsl.ast;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public record PathsNode(List<String> paths) {
|
||||||
|
}
|
75
parser/src/main/java/nu/zoom/dsl/cli/EndpointsCLI.java
Normal file
75
parser/src/main/java/nu/zoom/dsl/cli/EndpointsCLI.java
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
package nu.zoom.dsl.cli;
|
||||||
|
|
||||||
|
import nu.zoom.dsl.ast.ParserWrapper;
|
||||||
|
import picocli.CommandLine;
|
||||||
|
import picocli.CommandLine.Command;
|
||||||
|
import picocli.CommandLine.Option;
|
||||||
|
import picocli.CommandLine.Parameters;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
|
@Command(
|
||||||
|
name = "EndpointsCLI",
|
||||||
|
mixinStandardHelpOptions = true,
|
||||||
|
description = "Generate source code from an endpoints specification file."
|
||||||
|
)
|
||||||
|
public class EndpointsCLI implements Callable<Integer> {
|
||||||
|
@Parameters(index = "0", description = "The source endpoints DSL file.")
|
||||||
|
private Path file;
|
||||||
|
|
||||||
|
@Option(names = {"-t", "--template"}, description = "The template directory. Default is ~/endpoints-templates")
|
||||||
|
private Path templateDir = Paths.get(System.getProperty("user.dir"), "endpoints-templates");
|
||||||
|
|
||||||
|
@Option(names = {"-o", "--output"}, description = "The directory to write the generated code to. Default is ~/endpoints-output")
|
||||||
|
private Path outputDir = Paths.get(System.getProperty("user.dir"), "endpoints-output");
|
||||||
|
|
||||||
|
@Option(names = {"-v", "--verbose"}, description = "Print verbose debug messages.")
|
||||||
|
private Boolean verbose = false;
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
int exitCode = new CommandLine(new EndpointsCLI()).execute(args);
|
||||||
|
System.exit(exitCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer call() {
|
||||||
|
try {
|
||||||
|
validateTemplateDirectory();
|
||||||
|
validateInputFile();
|
||||||
|
validateOutputDirectory();
|
||||||
|
new ParserWrapper().parse(file);
|
||||||
|
return 0;
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println(e.getMessage());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateOutputDirectory() throws IOException {
|
||||||
|
if (Files.notExists(this.outputDir)) {
|
||||||
|
Files.createDirectories(this.outputDir);
|
||||||
|
}
|
||||||
|
if (!Files.isDirectory(this.outputDir)) {
|
||||||
|
throw new IllegalArgumentException("Output directory: '" + this.outputDir + " 'is not a directory.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateTemplateDirectory() throws IOException {
|
||||||
|
if (!Files.isDirectory(this.templateDir)) {
|
||||||
|
throw new IllegalArgumentException("Template directory '" + this.templateDir + "' is not a directory.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateInputFile() throws IOException {
|
||||||
|
if (Files.notExists(this.file)) {
|
||||||
|
throw new IllegalArgumentException("Input file '" + this.file + "' does not exist.");
|
||||||
|
}
|
||||||
|
if (!Files.isReadable(this.file) || !Files.isRegularFile(this.file)) {
|
||||||
|
throw new IllegalArgumentException("Input file '" + this.file + "' is not a readable file.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +0,0 @@
|
||||||
package nu.zoom.dsl.parser;
|
|
||||||
|
|
||||||
public class ParserFactory {
|
|
||||||
}
|
|
10
test01.endpoints
Normal file
10
test01.endpoints
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
config:foo
|
||||||
|
}
|
||||||
|
|
||||||
|
SomeType(
|
||||||
|
foo:bar
|
||||||
|
)
|
||||||
|
|
||||||
|
/some/endpoint < SomeType
|
||||||
|
/some/other/endpoint < SomeOtherType(bar:String)
|
Loading…
Add table
Reference in a new issue