Grammar for states and transitions

This commit is contained in:
Johan Maasing 2025-04-19 07:08:06 +02:00
parent 8050d35811
commit a546d257f3
5 changed files with 88 additions and 20 deletions

View file

@ -1,14 +0,0 @@
package nu.zoom.dsl.ast;
import java.util.List;
import java.util.Map;
public interface ParseTreeTransformer {
List<EndpointNode> getEndpoints();
Map<String,String> getConfig();
List<TypeNode> getDataTypes();
}

View file

@ -2,8 +2,10 @@ package nu.zoom.dsl.ast;
import nu.zoom.dsl.parser.StatesBaseVisitor; import nu.zoom.dsl.parser.StatesBaseVisitor;
import nu.zoom.dsl.parser.StatesParser; import nu.zoom.dsl.parser.StatesParser;
import org.antlr.v4.runtime.tree.TerminalNode;
import java.util.*; import java.util.*;
import java.util.stream.Stream;
public class StatesVisitorTransformer extends StatesBaseVisitor<StatesParser.DocumentContext> { public class StatesVisitorTransformer extends StatesBaseVisitor<StatesParser.DocumentContext> {
private final HashMap<String,String> config = new HashMap<>(); private final HashMap<String,String> config = new HashMap<>();
@ -29,9 +31,30 @@ public class StatesVisitorTransformer extends StatesBaseVisitor<StatesParser.Doc
return super.visitState(ctx); return super.visitState(ctx);
} }
@Override
public StatesParser.DocumentContext visitMessage(StatesParser.MessageContext ctx) {
String messageName = ctx.typeName().IDENTIFIER().getText() ;
List<FieldNode> 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<StateNode> getStates() { public Set<StateNode> getStates() {
// TODO: Calculate state nodes from this.transitions HashSet<StateNode> states = new HashSet<>();
return Set.of(); this.transitions.forEach((state,v)->{
HashSet<TransitionNode> transitionNodes = new HashSet<>();
v.forEach((to, message) -> transitionNodes.add(new TransitionNode(message, to)));
states.add(new StateNode(state, "", transitionNodes)) ;
}) ;
return states ;
} }
public Map<String,String> getConfig() { public Map<String,String> getConfig() {
@ -40,10 +63,28 @@ public class StatesVisitorTransformer extends StatesBaseVisitor<StatesParser.Doc
public Set<TypeNode> getTypes() { public Set<TypeNode> getTypes() {
// TODO calculate data types from NodeTypes and MessageTypes with duplicate check. // TODO calculate data types from NodeTypes and MessageTypes with duplicate check.
return Set.of() ; HashMap<String, TypeNode> 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<FieldNode> extractFields(StatesParser.TypeDeclarationContext declaration) { private List<FieldNode> extractFields(StatesParser.TypeDeclarationContext declaration) {
if (declaration == null) {
return Collections.emptyList();
}
return declaration return declaration
.typeField() .typeField()
.stream() .stream()
@ -53,4 +94,13 @@ public class StatesVisitorTransformer extends StatesBaseVisitor<StatesParser.Doc
) )
.toList(); .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() : "");
}
} }

View file

@ -26,7 +26,6 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
@Command( @Command(
@ -35,7 +34,7 @@ import java.util.concurrent.Callable;
description = "Generate source code from an endpoints specification file." description = "Generate source code from an endpoints specification file."
) )
public class EndpointsCLI implements Callable<Integer> { public class EndpointsCLI implements Callable<Integer> {
public static enum ParserType { public enum ParserType {
Endpoints, Endpoints,
States States
} }
@ -71,7 +70,7 @@ public class EndpointsCLI implements Callable<Integer> {
validateOutputDirectory(); validateOutputDirectory();
verbose("Parsing: " + file.toAbsolutePath()); verbose("Parsing: " + file.toAbsolutePath());
if (parser == null) { if (parser == null) {
if (file.getFileName().endsWith(".states")) { if (file.getFileName().toString().endsWith(".states")) {
parser = ParserType.States; parser = ParserType.States;
} }
} }

View file

@ -0,0 +1,29 @@
```
Copyright 2025 "Johan Maasing" <johan@zoom.nu>
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}
</#list>
</#list>
```

4
test01.states Normal file
View file

@ -0,0 +1,4 @@
{ title: SomeNodes }
start(s:S) -> middle: message(foo:bar),
middle -> middle: selfmessage,
middle -> end: endmessage(bar:baz)