Skip to content

Commit

Permalink
Merge branch 'gmlewis-dart-turnstile'
Browse files Browse the repository at this point in the history
  • Loading branch information
unclebob committed Jun 22, 2024
2 parents 9cb113b + 5675b47 commit 92077b1
Show file tree
Hide file tree
Showing 9 changed files with 286 additions and 3 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ build
*.iml
.idea
test_cases/java_turnstile/.idea

.dart_tool
.packages
*.lock
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
# SMC
## The State Machine Compiler

SMC is a Java application that translates a state transition table into a program that implements the described state machine. Output languages include Java, Go, C, and C++. Adding other languages is trivial.
SMC is a Java application that translates a state transition table into a program that implements the described state machine. Output languages include Java, Go, Dart, C, and C++. Adding other languages is trivial.

### Command Line
`ant compile && ant jar`

`java -jar smc.jar -l <language> -o <directory> -f <flags>`

* `<language>` is one of: `C`, `Cpp`, `Go`, or `Java`.
* `<language>` is one of: `C`, `Cpp`, `Dart`, `Go`, or `Java`.
* `<directory>` is the output directory. Your new state machine will be written there.
* `<flags>` currently for Java only. `package:package_name` will put the appropriate `package` statement in the generated code.

Expand Down
29 changes: 29 additions & 0 deletions src/smc/generators/DartCodeGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package smc.generators;

import smc.OptimizedStateMachine;
import smc.generators.nestedSwitchCaseGenerator.NSCNodeVisitor;
import smc.implementers.DartNestedSwitchCaseImplementer;

import java.io.IOException;
import java.nio.file.Files;
import java.util.Map;

public class DartCodeGenerator extends CodeGenerator {
private DartNestedSwitchCaseImplementer implementer;

public DartCodeGenerator(OptimizedStateMachine optimizedStateMachine,
String outputDirectory,
Map<String, String> flags) {
super(optimizedStateMachine, outputDirectory, flags);
implementer = new DartNestedSwitchCaseImplementer(flags);
}

protected NSCNodeVisitor getImplementer() {
return implementer;
}

public void writeFiles() throws IOException {
String outputFileName = optimizedStateMachine.header.fsm + ".dart";
Files.write(getOutputPath(outputFileName), implementer.getOutput().getBytes());
}
}
115 changes: 115 additions & 0 deletions src/smc/implementers/DartNestedSwitchCaseImplementer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package smc.implementers;

import smc.Utilities;
import smc.generators.nestedSwitchCaseGenerator.NSCNodeVisitor;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static smc.generators.nestedSwitchCaseGenerator.NSCNode.*;

public class DartNestedSwitchCaseImplementer implements NSCNodeVisitor {
private String fsmName;
private String actionsName;
private String output = "";
private List<Error> errors = new ArrayList<>();
private Map<String, String> flags;

public DartNestedSwitchCaseImplementer(Map<String, String> flags) {
this.flags = flags;
}

public void visit(SwitchCaseNode switchCaseNode) {
output += String.format("switch (%s) {\n", switchCaseNode.variableName);
switchCaseNode.generateCases(this);
output += "}\n";
}

public void visit(CaseNode caseNode) {
output += String.format("case %s.%s:\n",caseNode.switchName, caseNode.caseName);
caseNode.caseActionNode.accept(this);
output += "break;\n\n";
}

public void visit(FunctionCallNode functionCallNode) {
output += String.format("%s(", functionCallNode.functionName);
if (functionCallNode.argument != null) {
functionCallNode.argument.accept(this);
}
output += ");\n";
}

public void visit(EnumNode enumNode) {
output += String.format(
"\nenum %s {%s}\n\n",
enumNode.name,
Utilities.commaList(enumNode.enumerators));
}

public void visit(StatePropertyNode statePropertyNode) {
output += "State."+statePropertyNode.initialState;
}

public void visit(EventDelegatorsNode eventDelegatorsNode) {
for (String event : eventDelegatorsNode.events) {
output += String.format("\t%s() {_processEvent(Event.%s, \"%s\");}\n", event, event, event);
}
}

public void visit(FSMClassNode fsmClassNode) {
if (fsmClassNode.actionsName == null) {
errors.add(Error.NO_ACTIONS);
return;
}

fsmName = fsmClassNode.className;
actionsName = fsmClassNode.actionsName;

output += "import 'package:meta/meta.dart';\n\n";
output += String.format("import '%s.dart';\n", actionsName);

fsmClassNode.stateEnum.accept(this);
fsmClassNode.eventEnum.accept(this);

output += String.format("\n" +
"abstract class %s extends %s {\n" +
"\tState state;\n\n" +
"\t%s({@required this.state = ", fsmName, actionsName,fsmName);
fsmClassNode.stateProperty.accept(this);
output += "})\n\t : assert(state != null);\n\n";

fsmClassNode.delegators.accept(this);
output += "\n\tsetState(State s) {state=s;}\n\n";

fsmClassNode.handleEvent.accept(this);
output += "}\n";
}

public void visit(HandleEventNode handleEventNode) {
output += "\t_processEvent(final Event event, final String eventName) {\n";
handleEventNode.switchCase.accept(this);
output += "}\n\n";
}

public void visit(EnumeratorNode enumeratorNode) {
output += enumeratorNode.enumeration + "." + enumeratorNode.enumerator;
}

public void visit(DefaultCaseNode defaultCaseNode) {
output += String.format("" +
"default:\n" +
"unexpected_transition(\"%s\", eventName);\n" +
"break;\n", defaultCaseNode.state);
}

public String getOutput() {
return output;
}

public List<Error> getErrors() {
return errors;
}

public enum Error {NO_ACTIONS}
}
10 changes: 10 additions & 0 deletions test_cases/dart_turnstile/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
all : clean TwoCoinTurnstile.dart turnstile_test.dart
pub get
pub run test turnstile_test.dart

clean :
rm -f TwoCoinTurnstile.dart

TwoCoinTurnstile.dart : twoCoinTurnstile.sm
java -jar ../../build/jar/smc.jar -l Dart $?
dartfmt -w *.dart
8 changes: 8 additions & 0 deletions test_cases/dart_turnstile/TurnstileActions.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
abstract class TurnstileActions {
lock();
unlock();
alarmOn();
alarmOff();
thankyou();
unexpected_transition(final String state, final String event);
}
7 changes: 7 additions & 0 deletions test_cases/dart_turnstile/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
name: turnstile

dependencies:
meta: ^1.2.4

dev_dependencies:
test: ^1.15.7
86 changes: 86 additions & 0 deletions test_cases/dart_turnstile/turnstile_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// -*- compile-command: "pub run test turnstile_test.dart"; -*-

import 'package:test/test.dart';

import 'TurnstileActions.dart';
import 'TwoCoinTurnstile.dart';

class MyTwoCoinTurnstile extends TwoCoinTurnstile implements TurnstileActions {
String output;
MyTwoCoinTurnstile({this.output = ''});

lock() {
output += 'L';
}

unlock() {
output += 'U';
}

thankyou() {
output += 'T';
}

alarmOn() {
output += 'A';
}

alarmOff() {
output += 'O';
}

unexpected_transition(final String state, final String event) {
output += 'X($state,$event)';
}
}

main() {
MyTwoCoinTurnstile fsm;

setUp(() {
fsm = MyTwoCoinTurnstile();
});

test('NormalBehavior', () {
fsm.Coin();
fsm.Coin();
fsm.Pass();
expect(fsm.output, equals('UL'));
});

test('Alarm', () {
fsm.Pass();
expect(fsm.output, equals('A'));
});

test('Thankyou', () {
fsm.Coin();
fsm.Coin();
fsm.Coin();
expect(fsm.output, equals('UT'));
});

test('NormalManyThanksAndAlarm', () {
fsm.Coin();
fsm.Coin();
fsm.Pass();
fsm.Coin();
fsm.Coin();
fsm.Coin();
fsm.Pass();
fsm.Pass();
expect(fsm.output, equals('ULUTLA'));
});

test('Undefined', () {
fsm.Pass();
fsm.Pass();
expect(fsm.output, equals('AX(Alarming,Pass)'));
});

test('Reset', () {
fsm.Pass();
fsm.Reset();
expect(fsm.output, equals('AOL'));
});
}
25 changes: 25 additions & 0 deletions test_cases/dart_turnstile/twoCoinTurnstile.sm
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
Actions: TurnstileActions
FSM: TwoCoinTurnstile
Initial: Locked
{
(Base) Reset Locked lock

Locked : Base {
Pass Alarming -
Coin FirstCoin -
}

Alarming : Base <alarmOn >alarmOff {
- - -
}

FirstCoin : Base {
Pass Alarming -
Coin Unlocked unlock
}

Unlocked : Base {
Pass Locked lock
Coin - thankyou
}
}

0 comments on commit 92077b1

Please sign in to comment.