Added types declarations

This commit is contained in:
Johan Maasing 2025-03-18 21:27:26 +01:00
parent 620999a992
commit 857f9c63a6
Signed by: johan
GPG key ID: FFD31BABEE2DEED2
6 changed files with 165 additions and 53 deletions

View file

@ -32,6 +32,7 @@
-->
<execution>
<id>jjtree-javacc</id>
<phase>generate-sources</phase>
<goals>
<goal>jjtree-javacc</goal>
</goals>

View file

@ -0,0 +1,6 @@
package nu.zoom.tapir;
import java.util.List;
public record DataTypeNode(String name, List<FieldNode> fields) {
}

View file

@ -39,19 +39,25 @@ public class Generator implements Callable<Integer> {
validateTemplateDirectory();
validateInputFile();
validateOutputDirectory();
var rootNode = new TapirParser(Files.newBufferedReader(this.file)).endpoints();
var rootNode = new TapirParser(Files.newBufferedReader(this.file)).specification();
if (this.verbose) {
System.out.println("====== Parse Tree ======");
rootNode.dump("");
}
var endpoints = NodeTransformer.transform(rootNode);
if (endpoints.isEmpty()) {
NodeTransformer transformer = new NodeTransformer();
transformer.transform(rootNode);
if (transformer.getEndpoints().isEmpty()) {
System.err.println("No tapir endpoints found.");
return 2;
}
if (this.verbose) {
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);
});
}
@ -59,7 +65,7 @@ public class Generator implements Callable<Integer> {
this.verbose,
this.outputDir,
this.templateDir,
endpoints
transformer.getEndpoints()
);
targetGenerator.generate();
return 0;

View file

@ -9,14 +9,95 @@ import java.util.ArrayList;
import java.util.List;
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 {
ArrayList<EndpointNode> endpoints = new ArrayList<>();
for (int i = 0; i < rootNode.jjtGetNumChildren(); i++) {
SimpleNode endpoint = assertSimpleNode(rootNode.jjtGetChild(i));
endpoints.add(handleEndpoint(endpoint));
public List<EndpointNode> getEndpoints() {
return endpoints;
}
return endpoints ;
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);
}
}
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 {
@ -27,13 +108,13 @@ public class NodeTransformer {
SimpleNode pathsParseNode =
assertSimpleNodeType(
node.jjtGetChild(0),
TapirParserTreeConstants.JJTPATHS
TapirParserTreeConstants.JJTPATH
);
PathsNode pathsNode = handlePaths(pathsParseNode);
SimpleNode handlerParseNode =
assertSimpleNodeType(
node.jjtGetChild(1),
TapirParserTreeConstants.JJTHANDLERSPEC
TapirParserTreeConstants.JJTCOMPOUNDDATATYPE
);
HandlerNode handlerNode = handleHandler(handlerParseNode);
return new EndpointNode(pathsNode, handlerNode);
@ -47,25 +128,25 @@ public class NodeTransformer {
String handlerName = getStringValue(
assertSimpleNodeType(
handlerSpec.jjtGetChild(0),
TapirParserTreeConstants.JJTHANDLERNAME
TapirParserTreeConstants.JJTCOMPUNDDATATYPENAME
)
);
SimpleNode payloadFieldsParseNode =
assertSimpleNodeType(
handlerSpec.jjtGetChild(1),
TapirParserTreeConstants.JJTPAYLOADFIELDS
TapirParserTreeConstants.JJTDATATYPEFIELDS
);
List<FieldNode> fields = handleFields(payloadFieldsParseNode);
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<>();
for (int i = 0; i < payloadFieldsParseNode.jjtGetNumChildren(); i++) {
for (int i = 0; i < compoundDatatTypeFields.jjtGetNumChildren(); i++) {
SimpleNode payloadFieldParseNode =
assertSimpleNodeType(
payloadFieldsParseNode.jjtGetChild(i),
TapirParserTreeConstants.JJTPAYLOADFIELD
compoundDatatTypeFields.jjtGetChild(i),
TapirParserTreeConstants.JJTDATATYPEFIELDS
);
int numFieldNodes = payloadFieldParseNode.jjtGetNumChildren();
if (numFieldNodes != 2) {
@ -74,13 +155,13 @@ public class NodeTransformer {
String fieldName = getStringValue(
assertSimpleNodeType(
payloadFieldParseNode.jjtGetChild(0),
TapirParserTreeConstants.JJTPAYLOADFIELDNAME
TapirParserTreeConstants.JJTDATATYPEFIELDNAME
)
);
String fieldType = getStringValue(
assertSimpleNodeType(
payloadFieldParseNode.jjtGetChild(1),
TapirParserTreeConstants.JJTPAYLOADFIELDTYPE
TapirParserTreeConstants.JJTDATATYPEFIELDTYPE
)
);
fields.add(new FieldNode(fieldName, fieldType));

View file

@ -24,6 +24,7 @@ public class TargetGenerator {
public TargetGeneratorException(String message) {
super(message);
}
public TargetGeneratorException(Exception cause) {
super(cause);
}
@ -44,10 +45,11 @@ public class TargetGenerator {
templatePath,
"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 {
try {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_34);
cfg.setDirectoryForTemplateLoading(this.templatePath.toFile());
cfg.setDefaultEncoding("UTF-8");
@ -65,5 +67,8 @@ public class TargetGenerator {
templateData.put("endpoints", endpoints);
temp.process(templateData, outputFile);
}
} catch (TemplateException | IOException ex) {
throw new TargetGeneratorException(ex);
}
}
}

View file

@ -23,6 +23,7 @@ SKIP: {
TOKEN : {
<OPENPARANTHESIS: "(">
| <CLOSEPARANTHESIS: ")">
| <SEMICOLON: ";">
| <TRANSITION: "->">
| <SLASH: "/">
| <COLON: ":">
@ -32,63 +33,75 @@ TOKEN : {
| <IDENTIFIER: <FIRST_LETTER> (<LETTER>)* >
}
void path() :
void pathSegment() :
{Token t;}
{
t=<IDENTIFIER>{jjtThis.value = t.image;} <SLASH>
}
void paths() :
void path() :
{}
{
path() (path())*
pathSegment() (pathSegment())*
}
void payloadFieldName() :
void dataTypeFieldType() :
{Token t;}
{
t=<IDENTIFIER>{jjtThis.value = t.image;}
}
void payloadFieldType() :
void dataTypeFieldName() :
{Token t;}
{
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;}
{
t=<IDENTIFIER>{jjtThis.value = t.image;}
}
void handlerSpec() :
void compoundDataType() :
{}
{
handlerName() <OPENPARANTHESIS> payloadFields() <CLOSEPARANTHESIS>
compundDataTypeName() <OPENPARANTHESIS> dataTypeFields() <CLOSEPARANTHESIS>
}
void dataTypes() :
{}
{
(compoundDataType() )*
}
void endpoint() :
{}
{
paths() <TRANSITION> handlerSpec()
path() <TRANSITION> compoundDataType()
}
SimpleNode endpoints() :
void endpoints() :
{}
{
(endpoint() )*
}
SimpleNode specification() :
{}
{
dataTypes() endpoints()
{ return jjtThis; }
}