diff --git a/.gitignore b/.gitignore index 7acc024..48010bb 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,4 @@ build/ .vscode/ ### Mac OS ### -.DS_Store - -/endpoints-output/** \ No newline at end of file +.DS_Store \ No newline at end of file diff --git a/parser/pom.xml b/parser/pom.xml index a81607b..02c7c9c 100644 --- a/parser/pom.xml +++ b/parser/pom.xml @@ -57,7 +57,6 @@ org.apache.maven.plugins maven-jar-plugin - 3.4.2 diff --git a/parser/src/main/antlr4/imports/Common.g4 b/parser/src/main/antlr4/imports/Common.g4 deleted file mode 100644 index 8844343..0000000 --- a/parser/src/main/antlr4/imports/Common.g4 +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2025 "Johan Maasing" -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -grammar Common; -generatorconfig : '{' (configitem)? (',' configitem)* '}'; -configitem : configkey ':' configvalue ; -configkey : IDENTIFIER ; -configvalue : (IDENTIFIER|VALUE) ; -namedTypeDeclaration : typeName typeDeclaration ; -typeName : IDENTIFIER ; -typeDeclaration : '(' typeField (',' typeField)* ')' ; -typeField : fieldName COLON fieldType ; -fieldName : IDENTIFIER ; -fieldType : IDENTIFIER ; - -fragment LOWERCASE : [a-z] ; -fragment UPPERCASE : [A-Z] ; -fragment GENERICS : '['|']'|'<'|'>' ; -fragment DOT : '.' ; -fragment COMMENT_BEGIN : '/*' ; -fragment COMMENT_END : '*/' ; -fragment DIGIT : [0-9] ; - -WS : [ \t\n\r]+ -> skip; -COMMENT : COMMENT_BEGIN .*? COMMENT_END -> skip; -LEFT_ARROW : '<-' ; -RIGHT_ARROW : '->' ; -IDENTIFIER : (LOWERCASE | UPPERCASE) (LOWERCASE | UPPERCASE | DIGIT | GENERICS | DOT)* ; -VALUE : ~[ ,{}:()/="#';*\n\r\t]+ ; -SLASH : '/' ; -COLON : ':' ; \ No newline at end of file diff --git a/parser/src/main/antlr4/nu/zoom/dsl/parser/Endpoints.g4 b/parser/src/main/antlr4/nu/zoom/dsl/parser/Endpoints.g4 index 1a74607..4f47bf8 100644 --- a/parser/src/main/antlr4/nu/zoom/dsl/parser/Endpoints.g4 +++ b/parser/src/main/antlr4/nu/zoom/dsl/parser/Endpoints.g4 @@ -12,11 +12,35 @@ // See the License for the specific language governing permissions and // limitations under the License. grammar Endpoints; -import Common; - document : generatorconfig? (namedTypeDeclaration|endpoint)* ; -requestBody : LEFT_ARROW (namedTypeDeclaration | typeDeclaration | IDENTIFIER) ; -responseBody : RIGHT_ARROW (namedTypeDeclaration | typeDeclaration | IDENTIFIER) ; +generatorconfig : '{' (configitem)? (',' configitem)* '}'; +configitem : configkey ':' configvalue ; +configkey : IDENTIFIER ; +configvalue : (IDENTIFIER|VALUE) ; +namedTypeDeclaration : typeName typeDeclaration ; +typeName : IDENTIFIER ; +typeDeclaration : '(' typeField (',' typeField)* ')' ; +typeField : fieldName ':' fieldType ; +fieldName : IDENTIFIER ; +fieldType : IDENTIFIER ; +requestBody : REQUEST_PREFIX (namedTypeDeclaration | typeDeclaration | IDENTIFIER) ; +responseBody : RESPONSE_PREFIX (namedTypeDeclaration | typeDeclaration | IDENTIFIER) ; endpoint : path requestBody responseBody?; path : (pathSegment)+ ; pathSegment : SLASH (IDENTIFIER|VALUE) ; + + +fragment DIGIT : [0-9] ; +fragment LOWERCASE : [a-z] ; +fragment UPPERCASE : [A-Z] ; +fragment GENERICS : '['|']'|'<'|'>' ; +fragment DOT : '.' ; +fragment COMMENT_BEGIN : '/*' ; +fragment COMMENT_END : '*/' ; +WS : [ \t\n\r]+ -> skip; +COMMENT : COMMENT_BEGIN .*? COMMENT_END -> skip; +REQUEST_PREFIX : '<-' ; +RESPONSE_PREFIX : '->' ; +SLASH : '/' ; +IDENTIFIER : (LOWERCASE | UPPERCASE) (LOWERCASE | UPPERCASE | DIGIT | GENERICS | DOT)* ; +VALUE : ~[ ,{}:()/="#';*\n\r\t]+ ; diff --git a/parser/src/main/antlr4/nu/zoom/dsl/parser/States.g4 b/parser/src/main/antlr4/nu/zoom/dsl/parser/States.g4 deleted file mode 100644 index dbfe628..0000000 --- a/parser/src/main/antlr4/nu/zoom/dsl/parser/States.g4 +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2025 "Johan Maasing" -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -grammar States; -import Common; - -document : generatorconfig? transition (',' transition)* ; -transition : from RIGHT_ARROW to COLON message ; -from : state ; -to : state ; -message : typeName typeDeclaration? ; -state : typeName typeDeclaration? ; \ No newline at end of file diff --git a/parser/src/main/java/nu/zoom/dsl/ast/DocumentNode.java b/parser/src/main/java/nu/zoom/dsl/ast/DocumentNode.java index fcb8621..24ac244 100644 --- a/parser/src/main/java/nu/zoom/dsl/ast/DocumentNode.java +++ b/parser/src/main/java/nu/zoom/dsl/ast/DocumentNode.java @@ -15,11 +15,9 @@ package nu.zoom.dsl.ast; import java.util.List; import java.util.Map; -import java.util.Set; public record DocumentNode( Map config, - Set typeDefinitions, - List endpoints, - Set states) { + List typeDefinitions, + List endpoints) { } diff --git a/parser/src/main/java/nu/zoom/dsl/ast/EndpointsVisitorTransformer.java b/parser/src/main/java/nu/zoom/dsl/ast/EndpointsVisitorTransformer.java index e530a80..5d846d6 100644 --- a/parser/src/main/java/nu/zoom/dsl/ast/EndpointsVisitorTransformer.java +++ b/parser/src/main/java/nu/zoom/dsl/ast/EndpointsVisitorTransformer.java @@ -19,8 +19,7 @@ import org.antlr.v4.runtime.tree.TerminalNode; import java.util.*; -public class EndpointsVisitorTransformer - extends EndpointsBaseVisitor { +public class EndpointsVisitorTransformer extends EndpointsBaseVisitor { private final ArrayList endpoints = new ArrayList<>(); private final HashMap config = new HashMap<>(); private final HashSet dataTypes = new HashSet<>(); @@ -36,8 +35,8 @@ public class EndpointsVisitorTransformer return Map.copyOf(config); } - public Set getDataTypes() { - return Set.copyOf(dataTypes); + public List getDataTypes() { + return List.copyOf(dataTypes); } @Override @@ -131,7 +130,7 @@ public class EndpointsVisitorTransformer ).toList(); } - // Concatenate the text from two terminal nodes. Useful for contexts that are either an identifier or a value, + // Concatenate the text from to terminal nodes. Useful for contexts that are either an identifier or a value, // and you just want the text from whichever is not null. private String getText(TerminalNode identifier, TerminalNode value) { return diff --git a/parser/src/main/java/nu/zoom/dsl/ast/ParserWrapper.java b/parser/src/main/java/nu/zoom/dsl/ast/ParserWrapper.java index 11a95fd..2252cac 100644 --- a/parser/src/main/java/nu/zoom/dsl/ast/ParserWrapper.java +++ b/parser/src/main/java/nu/zoom/dsl/ast/ParserWrapper.java @@ -13,43 +13,23 @@ // limitations under the License. package nu.zoom.dsl.ast; -import nu.zoom.dsl.parser.*; +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.Path; -import java.util.List; -import java.util.Set; public class ParserWrapper { - public static DocumentNode parseEndpoints(Path sourcePath) throws IOException { + public static 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(); var astTransformer = new EndpointsVisitorTransformer(); astTransformer.visit(document); - return new DocumentNode( - astTransformer.getConfig(), - astTransformer.getDataTypes(), - astTransformer.getEndpoints(), - Set.of() - ); - } - public static DocumentNode parseStates(Path sourcePath) throws IOException { - var ins = CharStreams.fromPath(sourcePath, StandardCharsets.UTF_8); - StatesLexer lexer = new StatesLexer(ins); - StatesParser parser = new StatesParser(new CommonTokenStream(lexer)); - var document = parser.document(); - var astTransformer = new StatesVisitorTransformer(); - astTransformer.visit(document); - return new DocumentNode( - astTransformer.getConfig(), - astTransformer.getTypes(), - List.of(), - astTransformer.getStates() - ); + return new DocumentNode(astTransformer.getConfig(), astTransformer.getDataTypes(), astTransformer.getEndpoints()); } } diff --git a/parser/src/main/java/nu/zoom/dsl/ast/StateNode.java b/parser/src/main/java/nu/zoom/dsl/ast/StateNode.java deleted file mode 100644 index 5f8b7ff..0000000 --- a/parser/src/main/java/nu/zoom/dsl/ast/StateNode.java +++ /dev/null @@ -1,6 +0,0 @@ -package nu.zoom.dsl.ast; - -import java.util.Set; - -public record StateNode(String name, String data, Set transitions) { -} diff --git a/parser/src/main/java/nu/zoom/dsl/ast/StatesVisitorTransformer.java b/parser/src/main/java/nu/zoom/dsl/ast/StatesVisitorTransformer.java deleted file mode 100644 index e1360a7..0000000 --- a/parser/src/main/java/nu/zoom/dsl/ast/StatesVisitorTransformer.java +++ /dev/null @@ -1,106 +0,0 @@ -package nu.zoom.dsl.ast; - -import nu.zoom.dsl.parser.StatesBaseVisitor; -import nu.zoom.dsl.parser.StatesParser; -import org.antlr.v4.runtime.tree.TerminalNode; - -import java.util.*; -import java.util.stream.Stream; - -public class StatesVisitorTransformer extends StatesBaseVisitor { - private final HashMap config = new HashMap<>(); - private final HashSet nodeTypes = new HashSet<>(); - private final HashSet messageTypes = new HashSet<>(); - // from -> - private final HashMap> transitions = new HashMap<>(); - - @Override - public StatesParser.DocumentContext visitTransition(StatesParser.TransitionContext ctx) { - String from = ctx.from().state().typeName().IDENTIFIER().getText() ; - String to = ctx.to().state().typeName().IDENTIFIER().getText() ; - String message = ctx.message().typeName().IDENTIFIER().getText() ; - this.transitions.computeIfAbsent(from, k -> new HashMap<>()).put(to, message); - return super.visitTransition(ctx); - } - - @Override - public StatesParser.DocumentContext visitState(StatesParser.StateContext ctx) { - String stateName = ctx.typeName().IDENTIFIER().getText() ; - List fields = extractFields(ctx.typeDeclaration()) ; - this.nodeTypes.add(new TypeNode(stateName, fields)); - return super.visitState(ctx); - } - - @Override - public StatesParser.DocumentContext visitMessage(StatesParser.MessageContext ctx) { - String messageName = ctx.typeName().IDENTIFIER().getText() ; - List fields = extractFields(ctx.typeDeclaration()) ; - this.messageTypes.add(new TypeNode(messageName, fields)); - return super.visitMessage(ctx); - } - - @Override - public StatesParser.DocumentContext visitConfigitem(StatesParser.ConfigitemContext ctx) { - String configKey = ctx.configkey().IDENTIFIER().getText(); - String configValue = getText(ctx.configvalue().IDENTIFIER(), ctx.configvalue().VALUE()); - this.config.put(configKey, configValue); - return super.visitConfigitem(ctx); - } - - public Set getStates() { - HashSet states = new HashSet<>(); - this.transitions.forEach((state,v)->{ - HashSet transitionNodes = new HashSet<>(); - v.forEach((to, message) -> transitionNodes.add(new TransitionNode(message, to))); - states.add(new StateNode(state, "", transitionNodes)) ; - }) ; - return states ; - } - - public Map getConfig() { - return Map.copyOf(config); - } - - public Set getTypes() { - // TODO calculate data types from NodeTypes and MessageTypes with duplicate check. - HashMap typeNodes = new HashMap<>(); - this.nodeTypes.forEach(typeNode -> { - if (typeNodes.containsKey(typeNode.name())) { - throw new RuntimeException("Duplicate type name: " + typeNode.name()); - } else { - typeNodes.put(typeNode.name(), typeNode); - } - }) ; - this.messageTypes.forEach(typeNode -> { - if (typeNodes.containsKey(typeNode.name())) { - throw new RuntimeException("Duplicate type name: " + typeNode.name()); - } else { - typeNodes.put(typeNode.name(), typeNode); - } - }) ; - return Set.of(typeNodes.values().toArray(new TypeNode[0])) ; - } - - private List extractFields(StatesParser.TypeDeclarationContext declaration) { - if (declaration == null) { - return Collections.emptyList(); - } - return declaration - .typeField() - .stream() - .map( - ctx -> - new FieldNode(ctx.fieldName().getText(), ctx.fieldType().getText()) - ) - .toList(); - } - - // Concatenate the text from two terminal nodes. Useful for contexts that are either an identifier or a value, - // and you just want the text from whichever is not null. - private String getText(TerminalNode identifier, TerminalNode value) { - return - ((identifier != null) ? identifier.getText() : "") + - ((value != null) ? value.getText() : ""); - } - -} diff --git a/parser/src/main/java/nu/zoom/dsl/ast/TransitionNode.java b/parser/src/main/java/nu/zoom/dsl/ast/TransitionNode.java deleted file mode 100644 index 90ebd3e..0000000 --- a/parser/src/main/java/nu/zoom/dsl/ast/TransitionNode.java +++ /dev/null @@ -1,4 +0,0 @@ -package nu.zoom.dsl.ast; - -public record TransitionNode(String message, String toState) { -} diff --git a/parser/src/main/java/nu/zoom/dsl/cli/EndpointsCLI.java b/parser/src/main/java/nu/zoom/dsl/cli/EndpointsCLI.java index 724d9b4..b26cb9c 100644 --- a/parser/src/main/java/nu/zoom/dsl/cli/EndpointsCLI.java +++ b/parser/src/main/java/nu/zoom/dsl/cli/EndpointsCLI.java @@ -34,10 +34,6 @@ import java.util.concurrent.Callable; description = "Generate source code from an endpoints specification file." ) public class EndpointsCLI implements Callable { - public enum ParserType { - Endpoints, - States - } @SuppressWarnings("unused") @Parameters(index = "0", description = "The source endpoints DSL file.") private Path file; @@ -54,9 +50,6 @@ public class EndpointsCLI implements Callable { @Option(names = {"-v", "--verbose"}, description = "Print verbose debug messages.") private Boolean verbose = false; - @Option(names = {"-p", "--parser"}, description = "Force use of a specific parser instead of determining from filename. Valid values: ${COMPLETION-CANDIDATES}.") - private ParserType parser = null; - public static void main(String[] args) { int exitCode = new CommandLine(new EndpointsCLI()).execute(args); System.exit(exitCode); @@ -68,20 +61,8 @@ public class EndpointsCLI implements Callable { validateTemplateDirectory(); validateInputFile(); validateOutputDirectory(); - verbose("Parsing: " + file.toAbsolutePath()); - if (parser == null) { - if (file.getFileName().toString().endsWith(".states")) { - parser = ParserType.States; - } - } - final DocumentNode rootNode ; - if (parser == ParserType.States) { - verbose("using state grammar.") ; - rootNode = ParserWrapper.parseStates(file); - } else { - verbose("using endpoints grammar.") ; - rootNode = ParserWrapper.parseEndpoints(file); - } + verbose("Parsing " + file.toAbsolutePath()); + DocumentNode rootNode = ParserWrapper.parse(file); verbose("AST: " + rootNode); verbose("Generating from templates in: " + templateDir.toAbsolutePath()); Generator generator = new Generator(templateDir, rootNode, outputDir); diff --git a/states-templates/nodes.md.ftl b/states-templates/nodes.md.ftl deleted file mode 100644 index 89a5603..0000000 --- a/states-templates/nodes.md.ftl +++ /dev/null @@ -1,29 +0,0 @@ -``` -Copyright 2025 "Johan Maasing" - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -``` - -# The nodes - -```mermaid ---- -title: ${config.title} ---- -stateDiagram-v2 -<#list states as state> - <#list state.transitions as transition> - ${state.name} --> ${transition.toState} : ${transition.message} - - -``` \ No newline at end of file diff --git a/test01.states b/test01.states deleted file mode 100644 index d1f78a9..0000000 --- a/test01.states +++ /dev/null @@ -1,4 +0,0 @@ -{ title: SomeNodes } -start(s:S) -> middle: message(foo:bar), -middle -> middle: selfmessage, -middle -> end: endmessage(bar:baz) \ No newline at end of file