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.StatesParser;
import org.antlr.v4.runtime.tree.TerminalNode;
import java.util.*;
import java.util.stream.Stream;
public class StatesVisitorTransformer extends StatesBaseVisitor<StatesParser.DocumentContext> {
private final HashMap<String,String> config = new HashMap<>();
@ -29,9 +31,30 @@ public class StatesVisitorTransformer extends StatesBaseVisitor<StatesParser.Doc
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() {
// TODO: Calculate state nodes from this.transitions
return Set.of();
HashSet<StateNode> states = new HashSet<>();
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() {
@ -40,10 +63,28 @@ public class StatesVisitorTransformer extends StatesBaseVisitor<StatesParser.Doc
public Set<TypeNode> getTypes() {
// 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) {
if (declaration == null) {
return Collections.emptyList();
}
return declaration
.typeField()
.stream()
@ -53,4 +94,13 @@ public class StatesVisitorTransformer extends StatesBaseVisitor<StatesParser.Doc
)
.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.Paths;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.Callable;
@Command(
@ -35,7 +34,7 @@ import java.util.concurrent.Callable;
description = "Generate source code from an endpoints specification file."
)
public class EndpointsCLI implements Callable<Integer> {
public static enum ParserType {
public enum ParserType {
Endpoints,
States
}
@ -71,7 +70,7 @@ public class EndpointsCLI implements Callable<Integer> {
validateOutputDirectory();
verbose("Parsing: " + file.toAbsolutePath());
if (parser == null) {
if (file.getFileName().endsWith(".states")) {
if (file.getFileName().toString().endsWith(".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)