diff --git a/parser/pom.xml b/parser/pom.xml index e5601dc..b29ad05 100755 --- a/parser/pom.xml +++ b/parser/pom.xml @@ -32,6 +32,7 @@ --> jjtree-javacc + generate-sources jjtree-javacc diff --git a/parser/src/main/java/nu/zoom/tapir/DataTypeNode.java b/parser/src/main/java/nu/zoom/tapir/DataTypeNode.java new file mode 100644 index 0000000..923214e --- /dev/null +++ b/parser/src/main/java/nu/zoom/tapir/DataTypeNode.java @@ -0,0 +1,6 @@ +package nu.zoom.tapir; + +import java.util.List; + +public record DataTypeNode(String name, List fields) { +} diff --git a/parser/src/main/java/nu/zoom/tapir/Generator.java b/parser/src/main/java/nu/zoom/tapir/Generator.java index c48d20d..a068ca0 100755 --- a/parser/src/main/java/nu/zoom/tapir/Generator.java +++ b/parser/src/main/java/nu/zoom/tapir/Generator.java @@ -39,19 +39,25 @@ public class Generator implements Callable { 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 { this.verbose, this.outputDir, this.templateDir, - endpoints + transformer.getEndpoints() ); targetGenerator.generate(); return 0; diff --git a/parser/src/main/java/nu/zoom/tapir/NodeTransformer.java b/parser/src/main/java/nu/zoom/tapir/NodeTransformer.java index c4ed179..ffa5469 100644 --- a/parser/src/main/java/nu/zoom/tapir/NodeTransformer.java +++ b/parser/src/main/java/nu/zoom/tapir/NodeTransformer.java @@ -9,14 +9,95 @@ import java.util.ArrayList; import java.util.List; public class NodeTransformer { + private final List endpoints = new ArrayList<>(); + private final List dataTypes = new ArrayList<>(); - public static List transform(SimpleNode rootNode) throws ParseException { - ArrayList endpoints = new ArrayList<>(); - for (int i = 0; i < rootNode.jjtGetNumChildren(); i++) { - SimpleNode endpoint = assertSimpleNode(rootNode.jjtGetChild(i)); - endpoints.add(handleEndpoint(endpoint)); + public List getEndpoints() { + return endpoints; + } + + public List 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 handleEndpoints(SimpleNode endpoints) throws ParseException { + ArrayList 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 handleDataTypes(SimpleNode dataTypesDeclaration) throws ParseException { + List 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 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 fields = handleFields(payloadFieldsParseNode); return new HandlerNode(handlerName, fields); } - private static List handleFields(SimpleNode payloadFieldsParseNode) throws ParseException { + private static List handleFields(SimpleNode compoundDatatTypeFields) throws ParseException { ArrayList 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)); diff --git a/parser/src/main/java/nu/zoom/tapir/TargetGenerator.java b/parser/src/main/java/nu/zoom/tapir/TargetGenerator.java index 7c95f5a..307a063 100644 --- a/parser/src/main/java/nu/zoom/tapir/TargetGenerator.java +++ b/parser/src/main/java/nu/zoom/tapir/TargetGenerator.java @@ -24,6 +24,7 @@ public class TargetGenerator { public TargetGeneratorException(String message) { super(message); } + public TargetGeneratorException(Exception cause) { super(cause); } @@ -44,26 +45,30 @@ 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 { - Configuration cfg = new Configuration(Configuration.VERSION_2_3_34); - cfg.setDirectoryForTemplateLoading(this.templatePath.toFile()); - cfg.setDefaultEncoding("UTF-8"); - cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); - cfg.setLogTemplateExceptions(false); - cfg.setWrapUncheckedExceptions(true); - cfg.setFallbackOnNullLoopVariable(false); - Template temp = cfg.getTemplate(ENDPOINTS_TEMPLATE_NAME); - try (var outputFile = Files.newBufferedWriter( - outputPath.resolve("endpoints.scala"), - StandardOpenOption.CREATE, - StandardOpenOption.TRUNCATE_EXISTING - )) { - HashMap> templateData = new HashMap<>(); - templateData.put("endpoints", endpoints); - temp.process(templateData, outputFile); + public void generate() throws TargetGeneratorException { + try { + Configuration cfg = new Configuration(Configuration.VERSION_2_3_34); + cfg.setDirectoryForTemplateLoading(this.templatePath.toFile()); + cfg.setDefaultEncoding("UTF-8"); + cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); + cfg.setLogTemplateExceptions(false); + cfg.setWrapUncheckedExceptions(true); + cfg.setFallbackOnNullLoopVariable(false); + Template temp = cfg.getTemplate(ENDPOINTS_TEMPLATE_NAME); + try (var outputFile = Files.newBufferedWriter( + outputPath.resolve("endpoints.scala"), + StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING + )) { + HashMap> templateData = new HashMap<>(); + templateData.put("endpoints", endpoints); + temp.process(templateData, outputFile); + } + } catch (TemplateException | IOException ex) { + throw new TargetGeneratorException(ex); } } } diff --git a/parser/src/main/jjtree/tapir.jjt b/parser/src/main/jjtree/tapir.jjt index 72355da..efa5e5b 100755 --- a/parser/src/main/jjtree/tapir.jjt +++ b/parser/src/main/jjtree/tapir.jjt @@ -23,6 +23,7 @@ SKIP: { TOKEN : { | + | | "> | | @@ -32,63 +33,75 @@ TOKEN : { | ()* > } -void path() : +void pathSegment() : {Token t;} { t={jjtThis.value = t.image;} } -void paths() : +void path() : {} { - path() (path())* + pathSegment() (pathSegment())* } -void payloadFieldName() : +void dataTypeFieldType() : {Token t;} { t={jjtThis.value = t.image;} } -void payloadFieldType() : +void dataTypeFieldName() : {Token t;} { t={jjtThis.value = t.image;} } -void payloadField() : +void dataTypeField() : {} { - payloadFieldName() payloadFieldType() + dataTypeFieldName() dataTypeFieldType() } -void payloadFields() : +void dataTypeFields() : {} { - payloadField() ( payloadField() )* + dataTypeField() ( dataTypeField() )* } -void handlerName() : +void compundDataTypeName() : {Token t;} { t={jjtThis.value = t.image;} } -void handlerSpec() : +void compoundDataType() : {} { - handlerName() payloadFields() + compundDataTypeName() dataTypeFields() +} + +void dataTypes() : +{} +{ + (compoundDataType() )* } void endpoint() : {} { - paths() handlerSpec() + path() compoundDataType() } -SimpleNode endpoints() : +void endpoints() : {} { (endpoint() )* +} + +SimpleNode specification() : +{} +{ + dataTypes() endpoints() { return jjtThis; } }