Added types declarations
This commit is contained in:
parent
620999a992
commit
857f9c63a6
6 changed files with 165 additions and 53 deletions
|
@ -32,6 +32,7 @@
|
||||||
-->
|
-->
|
||||||
<execution>
|
<execution>
|
||||||
<id>jjtree-javacc</id>
|
<id>jjtree-javacc</id>
|
||||||
|
<phase>generate-sources</phase>
|
||||||
<goals>
|
<goals>
|
||||||
<goal>jjtree-javacc</goal>
|
<goal>jjtree-javacc</goal>
|
||||||
</goals>
|
</goals>
|
||||||
|
|
6
parser/src/main/java/nu/zoom/tapir/DataTypeNode.java
Normal file
6
parser/src/main/java/nu/zoom/tapir/DataTypeNode.java
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
package nu.zoom.tapir;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public record DataTypeNode(String name, List<FieldNode> fields) {
|
||||||
|
}
|
|
@ -39,19 +39,25 @@ public class Generator implements Callable<Integer> {
|
||||||
validateTemplateDirectory();
|
validateTemplateDirectory();
|
||||||
validateInputFile();
|
validateInputFile();
|
||||||
validateOutputDirectory();
|
validateOutputDirectory();
|
||||||
var rootNode = new TapirParser(Files.newBufferedReader(this.file)).endpoints();
|
var rootNode = new TapirParser(Files.newBufferedReader(this.file)).specification();
|
||||||
if (this.verbose) {
|
if (this.verbose) {
|
||||||
System.out.println("====== Parse Tree ======");
|
System.out.println("====== Parse Tree ======");
|
||||||
rootNode.dump("");
|
rootNode.dump("");
|
||||||
}
|
}
|
||||||
var endpoints = NodeTransformer.transform(rootNode);
|
NodeTransformer transformer = new NodeTransformer();
|
||||||
if (endpoints.isEmpty()) {
|
transformer.transform(rootNode);
|
||||||
|
if (transformer.getEndpoints().isEmpty()) {
|
||||||
System.err.println("No tapir endpoints found.");
|
System.err.println("No tapir endpoints found.");
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
if (this.verbose) {
|
if (this.verbose) {
|
||||||
System.out.println("\n====== AST ======");
|
System.out.println("\n====== AST ======");
|
||||||
endpoints.forEach(endpoint -> {
|
System.out.println("\n====== Types ======");
|
||||||
|
transformer.getDataTypes().forEach(type -> {
|
||||||
|
System.out.println(type);
|
||||||
|
});
|
||||||
|
System.out.println("\n====== Endpoints ======");
|
||||||
|
transformer.getEndpoints().forEach(endpoint -> {
|
||||||
System.out.println(endpoint);
|
System.out.println(endpoint);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -59,7 +65,7 @@ public class Generator implements Callable<Integer> {
|
||||||
this.verbose,
|
this.verbose,
|
||||||
this.outputDir,
|
this.outputDir,
|
||||||
this.templateDir,
|
this.templateDir,
|
||||||
endpoints
|
transformer.getEndpoints()
|
||||||
);
|
);
|
||||||
targetGenerator.generate();
|
targetGenerator.generate();
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -9,14 +9,95 @@ import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class NodeTransformer {
|
public class NodeTransformer {
|
||||||
|
private final List<EndpointNode> endpoints = new ArrayList<>();
|
||||||
|
private final List<DataTypeNode> dataTypes = new ArrayList<>();
|
||||||
|
|
||||||
public static List<EndpointNode> transform(SimpleNode rootNode) throws ParseException {
|
public List<EndpointNode> getEndpoints() {
|
||||||
ArrayList<EndpointNode> endpoints = new ArrayList<>();
|
return endpoints;
|
||||||
for (int i = 0; i < rootNode.jjtGetNumChildren(); i++) {
|
}
|
||||||
SimpleNode endpoint = assertSimpleNode(rootNode.jjtGetChild(i));
|
|
||||||
endpoints.add(handleEndpoint(endpoint));
|
public List<DataTypeNode> getDataTypes() {
|
||||||
|
return dataTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void transform(SimpleNode rootNode) throws ParseException {
|
||||||
|
assertSimpleNodeType(rootNode, TapirParserTreeConstants.JJTSPECIFICATION);
|
||||||
|
int numChildren = rootNode.jjtGetNumChildren();
|
||||||
|
if (numChildren == 2) {
|
||||||
|
this.dataTypes.addAll(
|
||||||
|
handleDataTypes(
|
||||||
|
assertSimpleNodeType(
|
||||||
|
rootNode.jjtGetChild(0),
|
||||||
|
TapirParserTreeConstants.JJTDATATYPES
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
this.endpoints.addAll(
|
||||||
|
handleEndpoints(
|
||||||
|
assertSimpleNodeType(
|
||||||
|
rootNode.jjtGetChild(1),
|
||||||
|
TapirParserTreeConstants.JJTENDPOINTS
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else if (numChildren == 1) {
|
||||||
|
this.endpoints.addAll(
|
||||||
|
handleEndpoints(
|
||||||
|
assertSimpleNodeType(
|
||||||
|
rootNode.jjtGetChild(1),
|
||||||
|
TapirParserTreeConstants.JJTENDPOINTS
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw new ParseException("Expected specification to have 1 or 2 children but had " + numChildren);
|
||||||
}
|
}
|
||||||
return endpoints ;
|
}
|
||||||
|
|
||||||
|
private static List<EndpointNode> handleEndpoints(SimpleNode endpoints) throws ParseException {
|
||||||
|
ArrayList<EndpointNode> endpointNodes = new ArrayList<>();
|
||||||
|
for (int i = 0; i < endpoints.jjtGetNumChildren(); i++) {
|
||||||
|
endpointNodes.add(
|
||||||
|
handleEndpoint(
|
||||||
|
assertSimpleNodeType(
|
||||||
|
endpoints.jjtGetChild(i),
|
||||||
|
TapirParserTreeConstants.JJTENDPOINT
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return endpointNodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<DataTypeNode> handleDataTypes(SimpleNode dataTypesDeclaration) throws ParseException {
|
||||||
|
List<DataTypeNode> dataTypes = new ArrayList<>();
|
||||||
|
for (int i = 0; i < dataTypesDeclaration.jjtGetNumChildren(); i++) {
|
||||||
|
dataTypes.add(
|
||||||
|
handleCompoundDataType(
|
||||||
|
assertSimpleNodeType(
|
||||||
|
dataTypesDeclaration.jjtGetChild(i),
|
||||||
|
TapirParserTreeConstants.JJTCOMPOUNDDATATYPE
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return dataTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DataTypeNode handleCompoundDataType(SimpleNode dataTypeNode) throws ParseException {
|
||||||
|
String typename = getStringValue(
|
||||||
|
assertSimpleNodeType(
|
||||||
|
dataTypeNode.jjtGetChild(0),
|
||||||
|
TapirParserTreeConstants.JJTCOMPUNDDATATYPENAME
|
||||||
|
)
|
||||||
|
);
|
||||||
|
List<FieldNode> fields = handleFields(
|
||||||
|
assertSimpleNodeType(
|
||||||
|
dataTypeNode.jjtGetChild(1),
|
||||||
|
TapirParserTreeConstants.JJTDATATYPEFIELDS
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return new DataTypeNode(typename, fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static EndpointNode handleEndpoint(SimpleNode node) throws ParseException {
|
private static EndpointNode handleEndpoint(SimpleNode node) throws ParseException {
|
||||||
|
@ -27,13 +108,13 @@ public class NodeTransformer {
|
||||||
SimpleNode pathsParseNode =
|
SimpleNode pathsParseNode =
|
||||||
assertSimpleNodeType(
|
assertSimpleNodeType(
|
||||||
node.jjtGetChild(0),
|
node.jjtGetChild(0),
|
||||||
TapirParserTreeConstants.JJTPATHS
|
TapirParserTreeConstants.JJTPATH
|
||||||
);
|
);
|
||||||
PathsNode pathsNode = handlePaths(pathsParseNode);
|
PathsNode pathsNode = handlePaths(pathsParseNode);
|
||||||
SimpleNode handlerParseNode =
|
SimpleNode handlerParseNode =
|
||||||
assertSimpleNodeType(
|
assertSimpleNodeType(
|
||||||
node.jjtGetChild(1),
|
node.jjtGetChild(1),
|
||||||
TapirParserTreeConstants.JJTHANDLERSPEC
|
TapirParserTreeConstants.JJTCOMPOUNDDATATYPE
|
||||||
);
|
);
|
||||||
HandlerNode handlerNode = handleHandler(handlerParseNode);
|
HandlerNode handlerNode = handleHandler(handlerParseNode);
|
||||||
return new EndpointNode(pathsNode, handlerNode);
|
return new EndpointNode(pathsNode, handlerNode);
|
||||||
|
@ -47,25 +128,25 @@ public class NodeTransformer {
|
||||||
String handlerName = getStringValue(
|
String handlerName = getStringValue(
|
||||||
assertSimpleNodeType(
|
assertSimpleNodeType(
|
||||||
handlerSpec.jjtGetChild(0),
|
handlerSpec.jjtGetChild(0),
|
||||||
TapirParserTreeConstants.JJTHANDLERNAME
|
TapirParserTreeConstants.JJTCOMPUNDDATATYPENAME
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
SimpleNode payloadFieldsParseNode =
|
SimpleNode payloadFieldsParseNode =
|
||||||
assertSimpleNodeType(
|
assertSimpleNodeType(
|
||||||
handlerSpec.jjtGetChild(1),
|
handlerSpec.jjtGetChild(1),
|
||||||
TapirParserTreeConstants.JJTPAYLOADFIELDS
|
TapirParserTreeConstants.JJTDATATYPEFIELDS
|
||||||
);
|
);
|
||||||
List<FieldNode> fields = handleFields(payloadFieldsParseNode);
|
List<FieldNode> fields = handleFields(payloadFieldsParseNode);
|
||||||
return new HandlerNode(handlerName, fields);
|
return new HandlerNode(handlerName, fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<FieldNode> handleFields(SimpleNode payloadFieldsParseNode) throws ParseException {
|
private static List<FieldNode> handleFields(SimpleNode compoundDatatTypeFields) throws ParseException {
|
||||||
ArrayList<FieldNode> fields = new ArrayList<>();
|
ArrayList<FieldNode> fields = new ArrayList<>();
|
||||||
for (int i = 0; i < payloadFieldsParseNode.jjtGetNumChildren(); i++) {
|
for (int i = 0; i < compoundDatatTypeFields.jjtGetNumChildren(); i++) {
|
||||||
SimpleNode payloadFieldParseNode =
|
SimpleNode payloadFieldParseNode =
|
||||||
assertSimpleNodeType(
|
assertSimpleNodeType(
|
||||||
payloadFieldsParseNode.jjtGetChild(i),
|
compoundDatatTypeFields.jjtGetChild(i),
|
||||||
TapirParserTreeConstants.JJTPAYLOADFIELD
|
TapirParserTreeConstants.JJTDATATYPEFIELDS
|
||||||
);
|
);
|
||||||
int numFieldNodes = payloadFieldParseNode.jjtGetNumChildren();
|
int numFieldNodes = payloadFieldParseNode.jjtGetNumChildren();
|
||||||
if (numFieldNodes != 2) {
|
if (numFieldNodes != 2) {
|
||||||
|
@ -74,13 +155,13 @@ public class NodeTransformer {
|
||||||
String fieldName = getStringValue(
|
String fieldName = getStringValue(
|
||||||
assertSimpleNodeType(
|
assertSimpleNodeType(
|
||||||
payloadFieldParseNode.jjtGetChild(0),
|
payloadFieldParseNode.jjtGetChild(0),
|
||||||
TapirParserTreeConstants.JJTPAYLOADFIELDNAME
|
TapirParserTreeConstants.JJTDATATYPEFIELDNAME
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
String fieldType = getStringValue(
|
String fieldType = getStringValue(
|
||||||
assertSimpleNodeType(
|
assertSimpleNodeType(
|
||||||
payloadFieldParseNode.jjtGetChild(1),
|
payloadFieldParseNode.jjtGetChild(1),
|
||||||
TapirParserTreeConstants.JJTPAYLOADFIELDTYPE
|
TapirParserTreeConstants.JJTDATATYPEFIELDTYPE
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
fields.add(new FieldNode(fieldName, fieldType));
|
fields.add(new FieldNode(fieldName, fieldType));
|
||||||
|
|
|
@ -24,6 +24,7 @@ public class TargetGenerator {
|
||||||
public TargetGeneratorException(String message) {
|
public TargetGeneratorException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TargetGeneratorException(Exception cause) {
|
public TargetGeneratorException(Exception cause) {
|
||||||
super(cause);
|
super(cause);
|
||||||
}
|
}
|
||||||
|
@ -44,26 +45,30 @@ public class TargetGenerator {
|
||||||
templatePath,
|
templatePath,
|
||||||
"Template path is required"
|
"Template path is required"
|
||||||
);
|
);
|
||||||
this.endpoints = Objects.requireNonNull(endpoints) ;
|
this.endpoints = Objects.requireNonNull(endpoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void generate() throws TargetGeneratorException, IOException, TemplateException {
|
public void generate() throws TargetGeneratorException {
|
||||||
Configuration cfg = new Configuration(Configuration.VERSION_2_3_34);
|
try {
|
||||||
cfg.setDirectoryForTemplateLoading(this.templatePath.toFile());
|
Configuration cfg = new Configuration(Configuration.VERSION_2_3_34);
|
||||||
cfg.setDefaultEncoding("UTF-8");
|
cfg.setDirectoryForTemplateLoading(this.templatePath.toFile());
|
||||||
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
|
cfg.setDefaultEncoding("UTF-8");
|
||||||
cfg.setLogTemplateExceptions(false);
|
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
|
||||||
cfg.setWrapUncheckedExceptions(true);
|
cfg.setLogTemplateExceptions(false);
|
||||||
cfg.setFallbackOnNullLoopVariable(false);
|
cfg.setWrapUncheckedExceptions(true);
|
||||||
Template temp = cfg.getTemplate(ENDPOINTS_TEMPLATE_NAME);
|
cfg.setFallbackOnNullLoopVariable(false);
|
||||||
try (var outputFile = Files.newBufferedWriter(
|
Template temp = cfg.getTemplate(ENDPOINTS_TEMPLATE_NAME);
|
||||||
outputPath.resolve("endpoints.scala"),
|
try (var outputFile = Files.newBufferedWriter(
|
||||||
StandardOpenOption.CREATE,
|
outputPath.resolve("endpoints.scala"),
|
||||||
StandardOpenOption.TRUNCATE_EXISTING
|
StandardOpenOption.CREATE,
|
||||||
)) {
|
StandardOpenOption.TRUNCATE_EXISTING
|
||||||
HashMap<String, List<EndpointNode>> templateData = new HashMap<>();
|
)) {
|
||||||
templateData.put("endpoints", endpoints);
|
HashMap<String, List<EndpointNode>> templateData = new HashMap<>();
|
||||||
temp.process(templateData, outputFile);
|
templateData.put("endpoints", endpoints);
|
||||||
|
temp.process(templateData, outputFile);
|
||||||
|
}
|
||||||
|
} catch (TemplateException | IOException ex) {
|
||||||
|
throw new TargetGeneratorException(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ SKIP: {
|
||||||
TOKEN : {
|
TOKEN : {
|
||||||
<OPENPARANTHESIS: "(">
|
<OPENPARANTHESIS: "(">
|
||||||
| <CLOSEPARANTHESIS: ")">
|
| <CLOSEPARANTHESIS: ")">
|
||||||
|
| <SEMICOLON: ";">
|
||||||
| <TRANSITION: "->">
|
| <TRANSITION: "->">
|
||||||
| <SLASH: "/">
|
| <SLASH: "/">
|
||||||
| <COLON: ":">
|
| <COLON: ":">
|
||||||
|
@ -32,63 +33,75 @@ TOKEN : {
|
||||||
| <IDENTIFIER: <FIRST_LETTER> (<LETTER>)* >
|
| <IDENTIFIER: <FIRST_LETTER> (<LETTER>)* >
|
||||||
}
|
}
|
||||||
|
|
||||||
void path() :
|
void pathSegment() :
|
||||||
{Token t;}
|
{Token t;}
|
||||||
{
|
{
|
||||||
t=<IDENTIFIER>{jjtThis.value = t.image;} <SLASH>
|
t=<IDENTIFIER>{jjtThis.value = t.image;} <SLASH>
|
||||||
}
|
}
|
||||||
|
|
||||||
void paths() :
|
void path() :
|
||||||
{}
|
{}
|
||||||
{
|
{
|
||||||
path() (path())*
|
pathSegment() (pathSegment())*
|
||||||
}
|
}
|
||||||
|
|
||||||
void payloadFieldName() :
|
void dataTypeFieldType() :
|
||||||
{Token t;}
|
{Token t;}
|
||||||
{
|
{
|
||||||
t=<IDENTIFIER>{jjtThis.value = t.image;}
|
t=<IDENTIFIER>{jjtThis.value = t.image;}
|
||||||
}
|
}
|
||||||
|
|
||||||
void payloadFieldType() :
|
void dataTypeFieldName() :
|
||||||
{Token t;}
|
{Token t;}
|
||||||
{
|
{
|
||||||
t=<IDENTIFIER>{jjtThis.value = t.image;}
|
t=<IDENTIFIER>{jjtThis.value = t.image;}
|
||||||
}
|
}
|
||||||
|
|
||||||
void payloadField() :
|
void dataTypeField() :
|
||||||
{}
|
{}
|
||||||
{
|
{
|
||||||
payloadFieldName() <COLON> payloadFieldType()
|
dataTypeFieldName() <COLON> dataTypeFieldType()
|
||||||
}
|
}
|
||||||
|
|
||||||
void payloadFields() :
|
void dataTypeFields() :
|
||||||
{}
|
{}
|
||||||
{
|
{
|
||||||
payloadField() (<COMMA> payloadField() )*
|
dataTypeField() (<COMMA> dataTypeField() )*
|
||||||
}
|
}
|
||||||
|
|
||||||
void handlerName() :
|
void compundDataTypeName() :
|
||||||
{Token t;}
|
{Token t;}
|
||||||
{
|
{
|
||||||
t=<IDENTIFIER>{jjtThis.value = t.image;}
|
t=<IDENTIFIER>{jjtThis.value = t.image;}
|
||||||
}
|
}
|
||||||
|
|
||||||
void handlerSpec() :
|
void compoundDataType() :
|
||||||
{}
|
{}
|
||||||
{
|
{
|
||||||
handlerName() <OPENPARANTHESIS> payloadFields() <CLOSEPARANTHESIS>
|
compundDataTypeName() <OPENPARANTHESIS> dataTypeFields() <CLOSEPARANTHESIS>
|
||||||
|
}
|
||||||
|
|
||||||
|
void dataTypes() :
|
||||||
|
{}
|
||||||
|
{
|
||||||
|
(compoundDataType() )*
|
||||||
}
|
}
|
||||||
|
|
||||||
void endpoint() :
|
void endpoint() :
|
||||||
{}
|
{}
|
||||||
{
|
{
|
||||||
paths() <TRANSITION> handlerSpec()
|
path() <TRANSITION> compoundDataType()
|
||||||
}
|
}
|
||||||
|
|
||||||
SimpleNode endpoints() :
|
void endpoints() :
|
||||||
{}
|
{}
|
||||||
{
|
{
|
||||||
(endpoint() )*
|
(endpoint() )*
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleNode specification() :
|
||||||
|
{}
|
||||||
|
{
|
||||||
|
dataTypes() endpoints()
|
||||||
{ return jjtThis; }
|
{ return jjtThis; }
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue