├── .github
└── workflows
│ └── ant.yml
├── .gitignore
├── README.md
├── build.xml
├── src
├── com
│ └── cleancoder
│ │ └── args
│ │ ├── Args.java
│ │ ├── ArgsException.java
│ │ ├── ArgumentMarshaler.java
│ │ ├── BooleanArgumentMarshaler.java
│ │ ├── DoubleArgumentMarshaler.java
│ │ ├── IntegerArgumentMarshaler.java
│ │ ├── MapArgumentMarshaler.java
│ │ ├── StringArgumentMarshaler.java
│ │ └── StringArrayArgumentMarshaler.java
└── smc
│ ├── OptimizedStateMachine.java
│ ├── SMC.java
│ ├── Utilities.java
│ ├── generators
│ ├── CCodeGenerator.java
│ ├── CodeGenerator.java
│ ├── CppCodeGenerator.java
│ ├── DartCodeGenerator.java
│ ├── GoCodeGenerator.java
│ ├── JavaCodeGenerator.java
│ └── nestedSwitchCaseGenerator
│ │ ├── NSCGenerator.java
│ │ ├── NSCNode.java
│ │ └── NSCNodeVisitor.java
│ ├── implementers
│ ├── CNestedSwitchCaseImplementer.java
│ ├── CppNestedSwitchCaseImplementer.java
│ ├── DartNestedSwitchCaseImplementer.java
│ ├── GoNestedSwitchCaseImplementer.java
│ └── JavaNestedSwitchCaseImplementer.java
│ ├── lexer
│ ├── Lexer.java
│ └── TokenCollector.java
│ ├── optimizer
│ └── Optimizer.java
│ ├── parser
│ ├── Builder.java
│ ├── FsmSyntax.java
│ ├── Parser.java
│ ├── ParserEvent.java
│ ├── ParserState.java
│ └── SyntaxBuilder.java
│ └── semanticAnalyzer
│ ├── SemanticAnalyzer.java
│ └── SemanticStateMachine.java
├── test
├── com
│ └── cleancoder
│ │ └── args
│ │ ├── ArgsExceptionTest.java
│ │ └── ArgsTest.java
└── smc
│ ├── UtilitiesTest.java
│ ├── generators
│ └── nestedSwitchCaseGenerator
│ │ └── NSCGeneratorTest.java
│ ├── implementers
│ ├── CNestedSwitchCaseImplementerTest.java
│ ├── CppNestedSwitchCaseImplementerTests.java
│ └── JavaNestedSwitchCaseImplementerTest.java
│ ├── lexer
│ └── LexerTest.java
│ ├── optimizer
│ └── OptimizerTest.java
│ ├── parser
│ └── ParserTest.java
│ └── semanticAnalyzer
│ └── SemanticAnalyzerTest.java
└── test_cases
├── Ice
├── RootFSMGen.java
└── ice.sm
├── c_turnstile
├── makefile
├── turnstile.h
├── turnstileActions.h
├── turnstile_test.c
└── twoCoinTurnstile.sm
├── cpp_turnstile
├── Makefile
├── TurnstileActions.h
├── turnstile_test.cc
└── twoCoinTurnstile.sm
├── dart_turnstile
├── Makefile
├── TurnstileActions.dart
├── pubspec.yaml
├── turnstile_test.dart
└── twoCoinTurnstile.sm
├── go_turnstile
├── Makefile
├── turnstile_actions.go
├── turnstile_test.go
└── two_coin_turnstile.sm
├── java_turnstile
├── .idea
│ └── .name
├── NSCTurnstile.iml
├── build.xml
└── src
│ ├── TurnstileActions.java
│ ├── TwoCoinTurnstileTest.java
│ └── twoCoinTurnstile.sm
└── simple turnstile
└── turnstile.sm
/.github/workflows/ant.yml:
--------------------------------------------------------------------------------
1 | # This workflow will build a Java project with Ant
2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-ant
3 |
4 | name: Java CI
5 |
6 | on:
7 | push:
8 | branches: [ "master" ]
9 | pull_request:
10 | branches: [ "master" ]
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: ubuntu-latest
16 |
17 | steps:
18 | - uses: actions/checkout@v4
19 | - name: Set up JDK 11
20 | uses: actions/setup-java@v3
21 | with:
22 | java-version: '11'
23 | distribution: 'temurin'
24 | - name: Build with Ant
25 | run: ant -noinput -buildfile build.xml
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | build
3 | *.iml
4 | .idea
5 | test_cases/java_turnstile/.idea
6 | .dart_tool
7 | .packages
8 | *.lock
9 | out
10 |
11 |
--------------------------------------------------------------------------------
/build.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/com/cleancoder/args/Args.java:
--------------------------------------------------------------------------------
1 | package com.cleancoder.args;
2 |
3 | import java.util.*;
4 |
5 | import static com.cleancoder.args.ArgsException.ErrorCode.*;
6 |
7 | public class Args {
8 | private Map marshalers;
9 | private Set argsFound;
10 | private ListIterator currentArgument;
11 |
12 | public Args(String schema, String[] args) throws ArgsException {
13 | marshalers = new HashMap();
14 | argsFound = new HashSet();
15 |
16 | parseSchema(schema);
17 | parseArgumentStrings(Arrays.asList(args));
18 | }
19 |
20 | private void parseSchema(String schema) throws ArgsException {
21 | for (String element : schema.split(","))
22 | if (element.length() > 0)
23 | parseSchemaElement(element.trim());
24 | }
25 |
26 | private void parseSchemaElement(String element) throws ArgsException {
27 | char elementId = element.charAt(0);
28 | String elementTail = element.substring(1);
29 | validateSchemaElementId(elementId);
30 | if (elementTail.length() == 0)
31 | marshalers.put(elementId, new BooleanArgumentMarshaler());
32 | else if (elementTail.equals("*"))
33 | marshalers.put(elementId, new StringArgumentMarshaler());
34 | else if (elementTail.equals("#"))
35 | marshalers.put(elementId, new IntegerArgumentMarshaler());
36 | else if (elementTail.equals("##"))
37 | marshalers.put(elementId, new DoubleArgumentMarshaler());
38 | else if (elementTail.equals("[*]"))
39 | marshalers.put(elementId, new StringArrayArgumentMarshaler());
40 | else if (elementTail.equals("&"))
41 | marshalers.put(elementId, new MapArgumentMarshaler());
42 | else
43 | throw new ArgsException(INVALID_ARGUMENT_FORMAT, elementId, elementTail);
44 | }
45 |
46 | private void validateSchemaElementId(char elementId) throws ArgsException {
47 | if (!Character.isLetter(elementId))
48 | throw new ArgsException(INVALID_ARGUMENT_NAME, elementId, null);
49 | }
50 |
51 | private void parseArgumentStrings(List argsList) throws ArgsException {
52 | for (currentArgument = argsList.listIterator(); currentArgument.hasNext();) {
53 | String argString = currentArgument.next();
54 | if (argString.startsWith("-")) {
55 | parseArgumentCharacters(argString.substring(1));
56 | } else {
57 | currentArgument.previous();
58 | break;
59 | }
60 | }
61 | }
62 |
63 | private void parseArgumentCharacters(String argChars) throws ArgsException {
64 | for (int i = 0; i < argChars.length(); i++)
65 | parseArgumentCharacter(argChars.charAt(i));
66 | }
67 |
68 | private void parseArgumentCharacter(char argChar) throws ArgsException {
69 | ArgumentMarshaler m = marshalers.get(argChar);
70 | if (m == null) {
71 | throw new ArgsException(UNEXPECTED_ARGUMENT, argChar, null);
72 | } else {
73 | argsFound.add(argChar);
74 | try {
75 | m.set(currentArgument);
76 | } catch (ArgsException e) {
77 | e.setErrorArgumentId(argChar);
78 | throw e;
79 | }
80 | }
81 | }
82 |
83 | public boolean has(char arg) {
84 | return argsFound.contains(arg);
85 | }
86 |
87 | public int nextArgument() {
88 | return currentArgument.nextIndex();
89 | }
90 |
91 | public boolean getBoolean(char arg) {
92 | return BooleanArgumentMarshaler.getValue(marshalers.get(arg));
93 | }
94 |
95 | public String getString(char arg) {
96 | return StringArgumentMarshaler.getValue(marshalers.get(arg));
97 | }
98 |
99 | public int getInt(char arg) {
100 | return IntegerArgumentMarshaler.getValue(marshalers.get(arg));
101 | }
102 |
103 | public double getDouble(char arg) {
104 | return DoubleArgumentMarshaler.getValue(marshalers.get(arg));
105 | }
106 |
107 | public String[] getStringArray(char arg) {
108 | return StringArrayArgumentMarshaler.getValue(marshalers.get(arg));
109 | }
110 |
111 | public Map getMap(char arg) {
112 | return MapArgumentMarshaler.getValue(marshalers.get(arg));
113 | }
114 | }
--------------------------------------------------------------------------------
/src/com/cleancoder/args/ArgsException.java:
--------------------------------------------------------------------------------
1 | package com.cleancoder.args;
2 |
3 | import static com.cleancoder.args.ArgsException.ErrorCode.*;
4 |
5 | public class ArgsException extends Exception {
6 | private char errorArgumentId = '\0';
7 | private String errorParameter = null;
8 | private ErrorCode errorCode = OK;
9 |
10 | public ArgsException() {}
11 |
12 | public ArgsException(String message) {super(message);}
13 |
14 | public ArgsException(ErrorCode errorCode) {
15 | this.errorCode = errorCode;
16 | }
17 |
18 | public ArgsException(ErrorCode errorCode, String errorParameter) {
19 | this.errorCode = errorCode;
20 | this.errorParameter = errorParameter;
21 | }
22 |
23 | public ArgsException(ErrorCode errorCode, char errorArgumentId, String errorParameter) {
24 | this.errorCode = errorCode;
25 | this.errorParameter = errorParameter;
26 | this.errorArgumentId = errorArgumentId;
27 | }
28 |
29 | public char getErrorArgumentId() {
30 | return errorArgumentId;
31 | }
32 |
33 | public void setErrorArgumentId(char errorArgumentId) {
34 | this.errorArgumentId = errorArgumentId;
35 | }
36 |
37 | public String getErrorParameter() {
38 | return errorParameter;
39 | }
40 |
41 | public void setErrorParameter(String errorParameter) {
42 | this.errorParameter = errorParameter;
43 | }
44 |
45 | public ErrorCode getErrorCode() {
46 | return errorCode;
47 | }
48 |
49 | public void setErrorCode(ErrorCode errorCode) {
50 | this.errorCode = errorCode;
51 | }
52 |
53 | public String errorMessage() {
54 | switch (errorCode) {
55 | case OK:
56 | return "TILT: Should not get here.";
57 | case UNEXPECTED_ARGUMENT:
58 | return String.format("Argument -%c unexpected.", errorArgumentId);
59 | case MISSING_STRING:
60 | return String.format("Could not find string parameter for -%c.", errorArgumentId);
61 | case INVALID_INTEGER:
62 | return String.format("Argument -%c expects an integer but was '%s'.", errorArgumentId, errorParameter);
63 | case MISSING_INTEGER:
64 | return String.format("Could not find integer parameter for -%c.", errorArgumentId);
65 | case INVALID_DOUBLE:
66 | return String.format("Argument -%c expects a double but was '%s'.", errorArgumentId, errorParameter);
67 | case MISSING_DOUBLE:
68 | return String.format("Could not find double parameter for -%c.", errorArgumentId);
69 | case INVALID_ARGUMENT_NAME:
70 | return String.format("'%c' is not a valid argument name.", errorArgumentId);
71 | case INVALID_ARGUMENT_FORMAT:
72 | return String.format("'%s' is not a valid argument format.", errorParameter);
73 | }
74 | return "";
75 | }
76 |
77 | public enum ErrorCode {
78 | OK, INVALID_ARGUMENT_FORMAT, UNEXPECTED_ARGUMENT, INVALID_ARGUMENT_NAME,
79 | MISSING_STRING,
80 | MISSING_INTEGER, INVALID_INTEGER,
81 | MISSING_DOUBLE, INVALID_DOUBLE,
82 | MALFORMED_MAP, MISSING_MAP
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/com/cleancoder/args/ArgumentMarshaler.java:
--------------------------------------------------------------------------------
1 | package com.cleancoder.args;
2 |
3 | import java.util.Iterator;
4 |
5 | public interface ArgumentMarshaler {
6 | void set(Iterator currentArgument) throws ArgsException;
7 | }
8 |
--------------------------------------------------------------------------------
/src/com/cleancoder/args/BooleanArgumentMarshaler.java:
--------------------------------------------------------------------------------
1 | package com.cleancoder.args;
2 |
3 | import java.util.Iterator;
4 |
5 | public class BooleanArgumentMarshaler implements ArgumentMarshaler {
6 | private boolean booleanValue = false;
7 |
8 | public void set(Iterator currentArgument) throws ArgsException {
9 | booleanValue = true;
10 | }
11 |
12 | public static boolean getValue(ArgumentMarshaler am) {
13 | if (am != null && am instanceof BooleanArgumentMarshaler)
14 | return ((BooleanArgumentMarshaler) am).booleanValue;
15 | else
16 | return false;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/com/cleancoder/args/DoubleArgumentMarshaler.java:
--------------------------------------------------------------------------------
1 | package com.cleancoder.args;
2 |
3 | import static com.cleancoder.args.ArgsException.ErrorCode.*;
4 |
5 | import java.util.*;
6 |
7 | public class DoubleArgumentMarshaler implements ArgumentMarshaler {
8 | private double doubleValue = 0;
9 |
10 | public void set(Iterator currentArgument) throws ArgsException {
11 | String parameter = null;
12 | try {
13 | parameter = currentArgument.next();
14 | doubleValue = Double.parseDouble(parameter);
15 | } catch (NoSuchElementException e) {
16 | throw new ArgsException(MISSING_DOUBLE);
17 | } catch (NumberFormatException e) {
18 | throw new ArgsException(INVALID_DOUBLE, parameter);
19 | }
20 | }
21 |
22 | public static double getValue(ArgumentMarshaler am) {
23 | if (am != null && am instanceof DoubleArgumentMarshaler)
24 | return ((DoubleArgumentMarshaler) am).doubleValue;
25 | else
26 | return 0.0;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/com/cleancoder/args/IntegerArgumentMarshaler.java:
--------------------------------------------------------------------------------
1 | package com.cleancoder.args;
2 |
3 | import static com.cleancoder.args.ArgsException.ErrorCode.*;
4 |
5 | import java.util.*;
6 |
7 | public class IntegerArgumentMarshaler implements ArgumentMarshaler {
8 | private int intValue = 0;
9 |
10 | public void set(Iterator currentArgument) throws ArgsException {
11 | String parameter = null;
12 | try {
13 | parameter = currentArgument.next();
14 | intValue = Integer.parseInt(parameter);
15 | } catch (NoSuchElementException e) {
16 | throw new ArgsException(MISSING_INTEGER);
17 | } catch (NumberFormatException e) {
18 | throw new ArgsException(INVALID_INTEGER, parameter);
19 | }
20 | }
21 |
22 | public static int getValue(ArgumentMarshaler am) {
23 | if (am != null && am instanceof IntegerArgumentMarshaler)
24 | return ((IntegerArgumentMarshaler) am).intValue;
25 | else
26 | return 0;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/com/cleancoder/args/MapArgumentMarshaler.java:
--------------------------------------------------------------------------------
1 | package com.cleancoder.args;
2 |
3 | import java.util.HashMap;
4 | import java.util.Iterator;
5 | import java.util.Map;
6 | import java.util.NoSuchElementException;
7 |
8 | import static com.cleancoder.args.ArgsException.ErrorCode.*;
9 |
10 | public class MapArgumentMarshaler implements ArgumentMarshaler {
11 | private Map map = new HashMap<>();
12 |
13 | public void set(Iterator currentArgument) throws ArgsException {
14 | try {
15 | String[] mapEntries = currentArgument.next().split(",");
16 | for (String entry : mapEntries) {
17 | String[] entryComponents = entry.split(":");
18 | if (entryComponents.length != 2)
19 | throw new ArgsException(MALFORMED_MAP);
20 | map.put(entryComponents[0], entryComponents[1]);
21 | }
22 | } catch (NoSuchElementException e) {
23 | throw new ArgsException(MISSING_MAP);
24 | }
25 | }
26 |
27 | public static Map getValue(ArgumentMarshaler am) {
28 | if (am != null && am instanceof MapArgumentMarshaler)
29 | return ((MapArgumentMarshaler) am).map;
30 | else
31 | return new HashMap<>();
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/com/cleancoder/args/StringArgumentMarshaler.java:
--------------------------------------------------------------------------------
1 | package com.cleancoder.args;
2 |
3 | import java.util.Iterator;
4 | import java.util.NoSuchElementException;
5 |
6 | import static com.cleancoder.args.ArgsException.ErrorCode.MISSING_STRING;
7 |
8 | public class StringArgumentMarshaler implements ArgumentMarshaler {
9 | private String stringValue = "";
10 |
11 | public void set(Iterator currentArgument) throws ArgsException {
12 | try {
13 | stringValue = currentArgument.next();
14 | } catch (NoSuchElementException e) {
15 | throw new ArgsException(MISSING_STRING);
16 | }
17 | }
18 |
19 | public static String getValue(ArgumentMarshaler am) {
20 | if (am != null && am instanceof StringArgumentMarshaler)
21 | return ((StringArgumentMarshaler) am).stringValue;
22 | else
23 | return "";
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/com/cleancoder/args/StringArrayArgumentMarshaler.java:
--------------------------------------------------------------------------------
1 | package com.cleancoder.args;
2 |
3 | import static com.cleancoder.args.ArgsException.ErrorCode.*;
4 |
5 | import java.util.*;
6 |
7 | public class StringArrayArgumentMarshaler implements ArgumentMarshaler {
8 | private List strings = new ArrayList();
9 |
10 | public void set(Iterator currentArgument) throws ArgsException {
11 | try {
12 | strings.add(currentArgument.next());
13 | } catch (NoSuchElementException e) {
14 | throw new ArgsException(MISSING_STRING);
15 | }
16 | }
17 |
18 | public static String[] getValue(ArgumentMarshaler am) {
19 | if (am != null && am instanceof StringArrayArgumentMarshaler)
20 | return ((StringArrayArgumentMarshaler) am).strings.toArray(new String[0]);
21 | else
22 | return new String[0];
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/smc/OptimizedStateMachine.java:
--------------------------------------------------------------------------------
1 | package smc;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | // This is the final output of the finite state machine parser.
7 | // Code generators will use this format as their input.
8 |
9 | public class OptimizedStateMachine {
10 | public List states = new ArrayList<>();
11 | public List events = new ArrayList<>();
12 | public List actions = new ArrayList<>();
13 | public Header header;
14 | public List transitions = new ArrayList<>();
15 |
16 | public String transitionsToString() {
17 | String result = "";
18 | for (Transition t : transitions)
19 | result += t;
20 | return result;
21 | }
22 |
23 | public String toString() {
24 | String transitionsString = transitionsToString().replaceAll("\n", "\n ");
25 | transitionsString = transitionsString.substring(0, transitionsString.length()-2);
26 | return String.format("Initial: %s\nFsm: %s\nActions:%s\n{\n %s}\n",
27 | header.initial, header.fsm, header.actions, transitionsString);
28 | }
29 |
30 | public static class Header {
31 | public String initial;
32 | public String fsm;
33 | public String actions;
34 | }
35 |
36 | public static class Transition {
37 | public String toString() {
38 | String result = String.format("%s {\n", currentState);
39 | for (SubTransition st : subTransitions)
40 | result += st.toString();
41 | result += "}\n";
42 | return result;
43 | }
44 |
45 | public String currentState;
46 | public List subTransitions = new ArrayList<>();
47 | }
48 |
49 | public static class SubTransition {
50 | public String toString() {
51 | return String.format(" %s %s {%s}\n", event, nextState, actionsToString());
52 | }
53 |
54 | private String actionsToString() {
55 | String result = "";
56 | if (actions.size() == 0)
57 | return result;
58 | for (String action : actions)
59 | result += action + " ";
60 | return result.substring(0, result.length()-1);
61 | }
62 |
63 | public String event;
64 | public String nextState;
65 | public List actions = new ArrayList<>();
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/smc/SMC.java:
--------------------------------------------------------------------------------
1 | package smc;
2 |
3 | import com.cleancoder.args.Args;
4 | import com.cleancoder.args.ArgsException;
5 | import smc.generators.CodeGenerator;
6 | import smc.lexer.Lexer;
7 | import smc.optimizer.Optimizer;
8 | import smc.parser.FsmSyntax;
9 | import smc.parser.Parser;
10 | import smc.parser.SyntaxBuilder;
11 | import smc.semanticAnalyzer.SemanticAnalyzer;
12 | import smc.semanticAnalyzer.SemanticStateMachine;
13 |
14 | import java.io.IOException;
15 | import java.lang.reflect.Constructor;
16 | import java.lang.reflect.InvocationTargetException;
17 | import java.nio.file.Files;
18 | import java.nio.file.Paths;
19 | import java.util.HashMap;
20 | import java.util.Map;
21 |
22 | import static smc.parser.ParserEvent.EOF;
23 |
24 | public class SMC {
25 | public static void main(String[] args) throws Exception {
26 | String argSchema = "o*,l*,f&";
27 |
28 | try {
29 | Args argParser = new Args(argSchema, args);
30 | new SmcCompiler(args, argParser).run();
31 | } catch (ArgsException e) {
32 | System.out.println("usage: " + argSchema + " file");
33 | System.out.println(e.errorMessage());
34 | System.exit(0);
35 | }
36 | }
37 |
38 | private static class SmcCompiler {
39 | private String[] args;
40 | private Args argParser;
41 | private String outputDirectory = null;
42 | private String language = "Java";
43 | Map flags = new HashMap<>();
44 | private SyntaxBuilder syntaxBuilder;
45 | private Parser parser;
46 | private Lexer lexer;
47 |
48 | public SmcCompiler(String[] args, Args argParser) {
49 | this.args = args;
50 | this.argParser = argParser;
51 | }
52 |
53 | public void run() throws IOException {
54 | extractCommandLineArguments();
55 |
56 | FsmSyntax fsm = compile(getSourceCode());
57 | int syntaxErrorCount = reportSyntaxErrors(fsm);
58 |
59 | if (syntaxErrorCount == 0) {
60 | generateCode(optimize(fsm));
61 | }
62 | }
63 |
64 | private void extractCommandLineArguments() {
65 | if (argParser.has('o'))
66 | outputDirectory = argParser.getString('o');
67 | if (argParser.has('l'))
68 | language = argParser.getString('l');
69 | if (argParser.has('f'))
70 | flags = argParser.getMap('f');
71 | }
72 |
73 | private FsmSyntax compile(String smContent) {
74 | syntaxBuilder = new SyntaxBuilder();
75 | parser = new Parser(syntaxBuilder);
76 | lexer = new Lexer(parser);
77 | lexer.lex(smContent);
78 | parser.handleEvent(EOF, -1, -1);
79 |
80 | return syntaxBuilder.getFsm();
81 | }
82 |
83 | private String getSourceCode() throws IOException {
84 | String sourceFileName = args[argParser.nextArgument()];
85 | return new String(Files.readAllBytes(Paths.get(sourceFileName)));
86 | }
87 |
88 | private int reportSyntaxErrors(FsmSyntax fsm) {
89 | int syntaxErrorCount = fsm.errors.size();
90 | System.out.println(String.format(
91 | "Compiled with %d syntax error%s.",
92 | syntaxErrorCount, (syntaxErrorCount == 1 ? "" : "s")));
93 |
94 | for (FsmSyntax.SyntaxError error : fsm.errors)
95 | System.out.println(error.toString());
96 | return syntaxErrorCount;
97 | }
98 |
99 | private OptimizedStateMachine optimize(FsmSyntax fsm) {
100 | SemanticStateMachine ast = new SemanticAnalyzer().analyze(fsm);
101 | return new Optimizer().optimize(ast);
102 | }
103 |
104 | private void generateCode(OptimizedStateMachine optimizedStateMachine) throws IOException {
105 | String generatorClassName = String.format("smc.generators.%sCodeGenerator", language);
106 | CodeGenerator generator = createGenerator(generatorClassName, optimizedStateMachine, outputDirectory, flags);
107 | generator.generate();
108 | }
109 |
110 | private CodeGenerator createGenerator(String generatorClassName,
111 | OptimizedStateMachine optimizedStateMachine,
112 | String outputDirectory,
113 | Map flags) {
114 | try {
115 | Class generatorClass = Class.forName(generatorClassName);
116 | Constructor constructor = generatorClass.getConstructor(OptimizedStateMachine.class, String.class, Map.class);
117 | return (CodeGenerator) constructor.newInstance(optimizedStateMachine, outputDirectory, flags);
118 | } catch (ClassNotFoundException e) {
119 | System.out.printf("The class %s was not found.\n", generatorClassName);
120 | System.exit(0);
121 | } catch (NoSuchMethodException e) {
122 | System.out.printf("Appropriate constructor for %s not found", generatorClassName);
123 | System.exit(0);
124 | } catch (InvocationTargetException | InstantiationException e) {
125 | e.printStackTrace();
126 | System.exit(0);
127 | } catch (IllegalAccessException e) {
128 | e.printStackTrace();
129 | System.exit(0);
130 | }
131 | return null;
132 | }
133 |
134 | }
135 |
136 | }
137 |
--------------------------------------------------------------------------------
/src/smc/Utilities.java:
--------------------------------------------------------------------------------
1 | package smc;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | public class Utilities {
7 | public static String commaList(List names) {
8 | String commaList = "";
9 | boolean first = true;
10 | for (String name : names) {
11 | commaList += (first ? "" : ",") + name;
12 | first = false;
13 | }
14 | return commaList;
15 | }
16 |
17 | public static String iotaList(String typeName, List names) {
18 | String iotaList = "";
19 | boolean first = true;
20 | for (String name : names) {
21 | iotaList += "\t" + name + (first ? " " + typeName + " = iota" : "") + "\n";
22 | first = false;
23 | }
24 | return iotaList;
25 | }
26 |
27 | public static String capitalize(String s) {
28 | if (s.length() < 2) return s.toUpperCase();
29 | return s.substring(0, 1).toUpperCase() + s.substring(1);
30 | }
31 |
32 | public static List addPrefix(String prefix, List list) {
33 | List result = new ArrayList<>();
34 | for (String element : list)
35 | result.add(prefix + element);
36 | return result;
37 | }
38 |
39 | public static String compressWhiteSpace(String s) {
40 | return s.replaceAll("\\n+", "\n").replaceAll("[\t ]+", " ").replaceAll(" *\n *", "\n");
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/smc/generators/CCodeGenerator.java:
--------------------------------------------------------------------------------
1 | package smc.generators;
2 |
3 | import smc.OptimizedStateMachine;
4 | import smc.generators.nestedSwitchCaseGenerator.NSCNodeVisitor;
5 | import smc.implementers.CNestedSwitchCaseImplementer;
6 |
7 | import java.io.IOException;
8 | import java.nio.file.Files;
9 | import java.util.Map;
10 |
11 | public class CCodeGenerator extends CodeGenerator {
12 | private CNestedSwitchCaseImplementer implementer;
13 |
14 | public CCodeGenerator(OptimizedStateMachine optimizedStateMachine,
15 | String outputDirectory,
16 | Map flags) {
17 | super(optimizedStateMachine, outputDirectory, flags);
18 | implementer = new CNestedSwitchCaseImplementer(flags);
19 | }
20 |
21 | protected NSCNodeVisitor getImplementer() {
22 | return implementer;
23 | }
24 |
25 | public void writeFiles() throws IOException {
26 | if (implementer.getErrors().size() > 0) {
27 | for (CNestedSwitchCaseImplementer.Error error : implementer.getErrors())
28 | System.out.println("Implementation error: " + error.name());
29 | } else {
30 | String fileName = toLowerCamelCase(optimizedStateMachine.header.fsm);
31 | Files.write(getOutputPath(fileName + ".h"), implementer.getFsmHeader().getBytes());
32 | Files.write(getOutputPath(fileName + ".c"), implementer.getFsmImplementation().getBytes());
33 | }
34 | }
35 |
36 | static private String toLowerCamelCase(String s) {
37 | if (s.length() < 2) return s.toLowerCase();
38 | return s.substring(0, 1).toLowerCase() + s.substring(1);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/smc/generators/CodeGenerator.java:
--------------------------------------------------------------------------------
1 | package smc.generators;
2 |
3 | import smc.OptimizedStateMachine;
4 | import smc.generators.nestedSwitchCaseGenerator.NSCGenerator;
5 | import smc.generators.nestedSwitchCaseGenerator.NSCNodeVisitor;
6 |
7 | import java.io.IOException;
8 | import java.nio.file.FileSystems;
9 | import java.nio.file.Path;
10 | import java.util.Map;
11 |
12 | public abstract class CodeGenerator {
13 | protected final OptimizedStateMachine optimizedStateMachine;
14 | protected final String outputDirectory;
15 | protected final Map flags;
16 |
17 | public CodeGenerator(OptimizedStateMachine optimizedStateMachine,
18 | String outputDirectory,
19 | Map flags) {
20 | this.optimizedStateMachine = optimizedStateMachine;
21 | this.outputDirectory = outputDirectory;
22 | this.flags = flags;
23 | }
24 |
25 | protected Path getOutputPath(String outputFileName) {
26 | Path outputPath;
27 | if (outputDirectory == null)
28 | outputPath = FileSystems.getDefault().getPath(outputFileName);
29 | else
30 | outputPath = FileSystems.getDefault().getPath(outputDirectory, outputFileName);
31 | return outputPath;
32 | }
33 |
34 | public void generate() throws IOException {
35 | NSCGenerator nscGenerator = new NSCGenerator();
36 | nscGenerator.generate(optimizedStateMachine).accept(getImplementer());
37 | writeFiles();
38 | }
39 |
40 | protected abstract NSCNodeVisitor getImplementer();
41 |
42 | protected abstract void writeFiles() throws IOException;
43 | }
44 |
--------------------------------------------------------------------------------
/src/smc/generators/CppCodeGenerator.java:
--------------------------------------------------------------------------------
1 | package smc.generators;
2 |
3 | import smc.OptimizedStateMachine;
4 | import smc.generators.nestedSwitchCaseGenerator.NSCNodeVisitor;
5 | import smc.implementers.CppNestedSwitchCaseImplementer;
6 |
7 | import java.io.IOException;
8 | import java.nio.file.Files;
9 | import java.util.Map;
10 |
11 | public class CppCodeGenerator extends CodeGenerator {
12 | private CppNestedSwitchCaseImplementer implementer;
13 |
14 | public CppCodeGenerator(OptimizedStateMachine optimizedStateMachine,
15 | String outputDirectory,
16 | Map flags) {
17 | super(optimizedStateMachine, outputDirectory, flags);
18 | implementer = new CppNestedSwitchCaseImplementer(flags);
19 | }
20 |
21 | protected NSCNodeVisitor getImplementer() {
22 | return implementer;
23 | }
24 |
25 | public void writeFiles() throws IOException {
26 | String outputFileName = optimizedStateMachine.header.fsm + ".h";
27 | Files.write(getOutputPath(outputFileName), implementer.getOutput().getBytes());
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/smc/generators/DartCodeGenerator.java:
--------------------------------------------------------------------------------
1 | package smc.generators;
2 |
3 | import smc.OptimizedStateMachine;
4 | import smc.generators.nestedSwitchCaseGenerator.NSCNodeVisitor;
5 | import smc.implementers.DartNestedSwitchCaseImplementer;
6 |
7 | import java.io.IOException;
8 | import java.nio.file.Files;
9 | import java.util.Map;
10 |
11 | public class DartCodeGenerator extends CodeGenerator {
12 | private DartNestedSwitchCaseImplementer implementer;
13 |
14 | public DartCodeGenerator(OptimizedStateMachine optimizedStateMachine,
15 | String outputDirectory,
16 | Map flags) {
17 | super(optimizedStateMachine, outputDirectory, flags);
18 | implementer = new DartNestedSwitchCaseImplementer(flags);
19 | }
20 |
21 | protected NSCNodeVisitor getImplementer() {
22 | return implementer;
23 | }
24 |
25 | public void writeFiles() throws IOException {
26 | String outputFileName = optimizedStateMachine.header.fsm + ".dart";
27 | Files.write(getOutputPath(outputFileName), implementer.getOutput().getBytes());
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/smc/generators/GoCodeGenerator.java:
--------------------------------------------------------------------------------
1 | package smc.generators;
2 |
3 | import smc.OptimizedStateMachine;
4 | import smc.generators.nestedSwitchCaseGenerator.NSCNodeVisitor;
5 | import smc.implementers.GoNestedSwitchCaseImplementer;
6 |
7 | import java.io.IOException;
8 | import java.nio.file.Files;
9 | import java.util.Map;
10 |
11 | public class GoCodeGenerator extends CodeGenerator {
12 | private GoNestedSwitchCaseImplementer implementer;
13 |
14 | public GoCodeGenerator(OptimizedStateMachine optimizedStateMachine,
15 | String outputDirectory,
16 | Map flags) {
17 | super(optimizedStateMachine, outputDirectory, flags);
18 | implementer = new GoNestedSwitchCaseImplementer(flags);
19 | }
20 |
21 | protected NSCNodeVisitor getImplementer() {
22 | return implementer;
23 | }
24 |
25 | public void writeFiles() throws IOException {
26 | String outputFileName = camelToSnake(optimizedStateMachine.header.fsm + ".go");
27 | Files.write(getOutputPath(outputFileName), implementer.getOutput().getBytes());
28 | }
29 |
30 | private static String camelToSnake(String s) {
31 | return s.replaceAll("([a-z])([A-Z]+)", "$1_$2").toLowerCase();
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/smc/generators/JavaCodeGenerator.java:
--------------------------------------------------------------------------------
1 | package smc.generators;
2 |
3 | import smc.OptimizedStateMachine;
4 | import smc.generators.nestedSwitchCaseGenerator.NSCNodeVisitor;
5 | import smc.implementers.JavaNestedSwitchCaseImplementer;
6 |
7 | import java.io.IOException;
8 | import java.nio.file.Files;
9 | import java.util.Map;
10 |
11 | public class JavaCodeGenerator extends CodeGenerator {
12 | private JavaNestedSwitchCaseImplementer implementer;
13 |
14 | public JavaCodeGenerator(OptimizedStateMachine optimizedStateMachine,
15 | String outputDirectory,
16 | Map flags) {
17 | super(optimizedStateMachine, outputDirectory, flags);
18 | implementer = new JavaNestedSwitchCaseImplementer(flags);
19 | }
20 |
21 | protected NSCNodeVisitor getImplementer() {
22 | return implementer;
23 | }
24 |
25 | public void writeFiles() throws IOException {
26 | String outputFileName = optimizedStateMachine.header.fsm + ".java";
27 | Files.write(getOutputPath(outputFileName), implementer.getOutput().getBytes());
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/smc/generators/nestedSwitchCaseGenerator/NSCGenerator.java:
--------------------------------------------------------------------------------
1 | package smc.generators.nestedSwitchCaseGenerator;
2 |
3 | import smc.OptimizedStateMachine;
4 |
5 | public class NSCGenerator {
6 | private NSCNode.EnumNode stateEnumNode;
7 | private NSCNode.EnumNode eventEnumNode;
8 | private NSCNode.EventDelegatorsNode eventDelegatorsNode;
9 | private NSCNode.StatePropertyNode statePropertyNode;
10 | private NSCNode.HandleEventNode handleEventNode;
11 | private NSCNode.SwitchCaseNode stateSwitch;
12 |
13 | public NSCNode generate(OptimizedStateMachine sm) {
14 | eventDelegatorsNode = new NSCNode.EventDelegatorsNode(sm.events);
15 | statePropertyNode = new NSCNode.StatePropertyNode(sm.header.initial);
16 | stateEnumNode = new NSCNode.EnumNode("State", sm.states);
17 | eventEnumNode = new NSCNode.EnumNode("Event", sm.events);
18 | stateSwitch = new NSCNode.SwitchCaseNode("state");
19 | addStateCases(sm);
20 | handleEventNode = new NSCNode.HandleEventNode(stateSwitch);
21 | return makeFsmNode(sm);
22 | }
23 |
24 | private NSCNode.FSMClassNode makeFsmNode(OptimizedStateMachine sm) {
25 | NSCNode.FSMClassNode fsm = new NSCNode.FSMClassNode();
26 | fsm.className = sm.header.fsm;
27 | fsm.actionsName = sm.header.actions;
28 | fsm.stateEnum = stateEnumNode;
29 | fsm.eventEnum = eventEnumNode;
30 | fsm.delegators = eventDelegatorsNode;
31 | fsm.stateProperty = statePropertyNode;
32 | fsm.handleEvent = handleEventNode;
33 | fsm.actions = sm.actions;
34 | fsm.states = sm.states;
35 | return fsm;
36 | }
37 |
38 | private void addStateCases(OptimizedStateMachine sm) {
39 | for (OptimizedStateMachine.Transition t : sm.transitions)
40 | addStateCase(stateSwitch, t);
41 | }
42 |
43 | private void addStateCase(NSCNode.SwitchCaseNode stateSwitch, OptimizedStateMachine.Transition t) {
44 | NSCNode.CaseNode stateCaseNode = new NSCNode.CaseNode("State", t.currentState);
45 | addEventCases(stateCaseNode, t);
46 | stateSwitch.caseNodes.add(stateCaseNode);
47 | }
48 |
49 | private void addEventCases(NSCNode.CaseNode stateCaseNode, OptimizedStateMachine.Transition t) {
50 | NSCNode.SwitchCaseNode eventSwitch = new NSCNode.SwitchCaseNode("event");
51 | stateCaseNode.caseActionNode = eventSwitch;
52 | for (OptimizedStateMachine.SubTransition st : t.subTransitions)
53 | addEventCase(eventSwitch, st);
54 | eventSwitch.caseNodes.add(new NSCNode.DefaultCaseNode(t.currentState));
55 | }
56 |
57 | private void addEventCase(NSCNode.SwitchCaseNode eventSwitch, OptimizedStateMachine.SubTransition st) {
58 | NSCNode.CaseNode eventCaseNode = new NSCNode.CaseNode("Event", st.event);
59 | addActions(st, eventCaseNode);
60 | eventSwitch.caseNodes.add(eventCaseNode);
61 | }
62 |
63 | private void addActions(OptimizedStateMachine.SubTransition st, NSCNode.CaseNode eventCaseNode) {
64 | NSCNode.CompositeNode actions = new NSCNode.CompositeNode();
65 | addSetStateNode(st.nextState, actions);
66 | for (String action : st.actions)
67 | actions.add(new NSCNode.FunctionCallNode(action));
68 |
69 | eventCaseNode.caseActionNode = actions;
70 | }
71 |
72 | private void addSetStateNode(String stateName, NSCNode.CompositeNode actions) {
73 | NSCNode.EnumeratorNode enumeratorNode = new NSCNode.EnumeratorNode("State", stateName);
74 | NSCNode.FunctionCallNode setStateNode = new NSCNode.FunctionCallNode("setState", enumeratorNode);
75 | actions.add(setStateNode);
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/smc/generators/nestedSwitchCaseGenerator/NSCNode.java:
--------------------------------------------------------------------------------
1 | package smc.generators.nestedSwitchCaseGenerator;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | public interface NSCNode {
7 | public void accept(NSCNodeVisitor visitor);
8 |
9 | public static class SwitchCaseNode implements NSCNode {
10 | public String variableName;
11 | public List caseNodes = new ArrayList<>();
12 |
13 | public SwitchCaseNode(String variableName) {
14 | this.variableName = variableName;
15 | }
16 |
17 | public void accept(NSCNodeVisitor visitor) {
18 | visitor.visit(this);
19 | }
20 |
21 | public void generateCases(NSCNodeVisitor visitor) {
22 | for (NSCNode c : caseNodes) {
23 | c.accept(visitor);
24 | }
25 | }
26 | }
27 |
28 | public static class CaseNode implements NSCNode {
29 | public String switchName;
30 | public String caseName;
31 | public NSCNode caseActionNode;
32 |
33 | public CaseNode(String SwitchName, String caseName){
34 | switchName = SwitchName;
35 | this.caseName = caseName;
36 | }
37 |
38 | public void accept(NSCNodeVisitor visitor) {
39 | visitor.visit(this);
40 | }
41 | }
42 |
43 | public class FunctionCallNode implements NSCNode {
44 | public String functionName;
45 | public NSCNode argument;
46 |
47 | public FunctionCallNode(String functionName) {
48 | this.functionName = functionName;
49 | }
50 |
51 | public FunctionCallNode(String functionName, NSCNode argument) {
52 | this.functionName = functionName;
53 | this.argument = argument;
54 | }
55 |
56 | public void accept(NSCNodeVisitor visitor) {
57 | visitor.visit(this);
58 | }
59 | }
60 |
61 | public class CompositeNode implements NSCNode {
62 | private List nodes = new ArrayList<>();
63 |
64 | public void accept(NSCNodeVisitor visitor) {
65 | for (NSCNode node : nodes)
66 | node.accept(visitor);
67 | }
68 |
69 | public void add(NSCNode node) {
70 | nodes.add(node);
71 | }
72 | }
73 |
74 | public class EnumNode implements NSCNode {
75 | public String name;
76 | public List enumerators;
77 |
78 | public EnumNode(String name, List enumerators) {
79 | this.name = name;
80 | this.enumerators = enumerators;
81 | }
82 |
83 | public void accept(NSCNodeVisitor visitor) {
84 | visitor.visit(this);
85 | }
86 | }
87 |
88 | public class StatePropertyNode implements NSCNode {
89 | public String initialState;
90 |
91 | public StatePropertyNode(String initialState) {
92 | this.initialState = initialState;
93 | }
94 |
95 | public void accept(NSCNodeVisitor visitor) {
96 | visitor.visit(this);
97 | }
98 | }
99 |
100 | public class EventDelegatorsNode implements NSCNode {
101 | public List events;
102 |
103 | public EventDelegatorsNode(List events) {
104 | this.events = events;
105 | }
106 |
107 | public void accept(NSCNodeVisitor visitor) {
108 | visitor.visit(this);
109 | }
110 | }
111 |
112 | public class FSMClassNode implements NSCNode {
113 | public EventDelegatorsNode delegators;
114 | public EnumNode eventEnum;
115 | public EnumNode stateEnum;
116 | public StatePropertyNode stateProperty;
117 | public HandleEventNode handleEvent;
118 | public String className;
119 | public String actionsName;
120 | public List actions;
121 | public List states;
122 |
123 | public void accept(NSCNodeVisitor visitor) {
124 | visitor.visit(this);
125 | }
126 | }
127 |
128 | public class HandleEventNode implements NSCNode {
129 | public SwitchCaseNode switchCase;
130 |
131 | public HandleEventNode(SwitchCaseNode switchCase) {
132 | this.switchCase = switchCase;
133 | }
134 |
135 | public void accept(NSCNodeVisitor visitor) {
136 | visitor.visit(this);
137 | }
138 | }
139 |
140 | public class EnumeratorNode implements NSCNode {
141 | public String enumeration;
142 | public String enumerator;
143 |
144 | public EnumeratorNode(String enumeration, String enumerator) {
145 | this.enumeration = enumeration;
146 | this.enumerator = enumerator;
147 | }
148 |
149 | public void accept(NSCNodeVisitor visitor) {
150 | visitor.visit(this);
151 | }
152 | }
153 |
154 | public class DefaultCaseNode implements NSCNode {
155 | public String state;
156 |
157 | public DefaultCaseNode(String state) {
158 | this.state = state;
159 | }
160 |
161 | public void accept(NSCNodeVisitor visitor) {
162 | visitor.visit(this);
163 | }
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/src/smc/generators/nestedSwitchCaseGenerator/NSCNodeVisitor.java:
--------------------------------------------------------------------------------
1 | package smc.generators.nestedSwitchCaseGenerator;
2 |
3 | public interface NSCNodeVisitor {
4 | void visit(NSCNode.SwitchCaseNode switchCaseNode);
5 | void visit(NSCNode.CaseNode caseNode);
6 | void visit(NSCNode.FunctionCallNode functionCallNode);
7 | void visit(NSCNode.EnumNode enumNode);
8 | void visit(NSCNode.StatePropertyNode statePropertyNode);
9 | void visit(NSCNode.EventDelegatorsNode eventDelegatorsNode);
10 | void visit(NSCNode.FSMClassNode fsmClassNode);
11 | void visit(NSCNode.HandleEventNode handleEventNode);
12 | void visit(NSCNode.EnumeratorNode enumeratorNode);
13 | void visit(NSCNode.DefaultCaseNode defaultCaseNode);
14 | }
15 |
--------------------------------------------------------------------------------
/src/smc/implementers/CNestedSwitchCaseImplementer.java:
--------------------------------------------------------------------------------
1 | package smc.implementers;
2 |
3 | import smc.Utilities;
4 | import smc.generators.nestedSwitchCaseGenerator.NSCNodeVisitor;
5 |
6 | import java.util.ArrayList;
7 | import java.util.List;
8 | import java.util.Map;
9 |
10 | import static smc.generators.nestedSwitchCaseGenerator.NSCNode.*;
11 |
12 | public class CNestedSwitchCaseImplementer implements NSCNodeVisitor {
13 | private String fsmName;
14 | private String actionsName;
15 | private String fsmHeader = "";
16 | private String fsmImplementation = "";
17 | private List errors = new ArrayList<>();
18 | private Map flags;
19 |
20 | public CNestedSwitchCaseImplementer(Map flags) {
21 | this.flags = flags;
22 | }
23 |
24 | public void visit(SwitchCaseNode switchCaseNode) {
25 | fsmImplementation += String.format("switch (%s) {\n", switchCaseNode.variableName);
26 | switchCaseNode.generateCases(this);
27 | fsmImplementation += "}\n";
28 | }
29 |
30 | public void visit(CaseNode caseNode) {
31 | fsmImplementation += String.format("case %s:\n", caseNode.caseName);
32 | caseNode.caseActionNode.accept(this);
33 | fsmImplementation += "break;\n\n";
34 | }
35 |
36 | public void visit(FunctionCallNode functionCallNode) {
37 | fsmImplementation += String.format("%s(fsm", functionCallNode.functionName);
38 | if (functionCallNode.argument != null) {
39 | fsmImplementation += ", ";
40 | functionCallNode.argument.accept(this);
41 | }
42 | fsmImplementation += ");\n";
43 | }
44 |
45 | public void visit(EnumNode enumNode) {
46 | fsmImplementation += String.format("enum %s {%s};\n", enumNode.name, Utilities.commaList(enumNode.enumerators));
47 | }
48 |
49 | public void visit(StatePropertyNode statePropertyNode) {
50 | fsmImplementation +=
51 | String.format("struct %s *make_%s(struct %s* actions) {\n", fsmName, fsmName, actionsName) +
52 | String.format("\tstruct %s *fsm = malloc(sizeof(struct %s));\n", fsmName, fsmName) +
53 | String.format("\tfsm->actions = actions;\n") +
54 | String.format("\tfsm->state = %s;\n", statePropertyNode.initialState) +
55 | String.format("\treturn fsm;\n") + "}\n\n";
56 |
57 | fsmImplementation += String.format("" +
58 | "static void setState(struct %s *fsm, enum State state) {\n" +
59 | "\tfsm->state = state;\n" +
60 | "}\n\n", fsmName);
61 | }
62 |
63 | public void visit(EventDelegatorsNode eventDelegatorsNode) {
64 | for (String event : eventDelegatorsNode.events) {
65 | fsmHeader += String.format("void %s_%s(struct %s*);\n", fsmName, event, fsmName);
66 |
67 | fsmImplementation += String.format("" +
68 | "void %s_%s(struct %s* fsm) {\n" +
69 | "\tprocessEvent(fsm->state, %s, fsm, \"%s\");\n" +
70 | "}\n", fsmName, event, fsmName, event, event);
71 | }
72 | }
73 |
74 | public void visit(FSMClassNode fsmClassNode) {
75 | if (fsmClassNode.actionsName == null) {
76 | errors.add(Error.NO_ACTION);
77 | return;
78 | }
79 | actionsName = fsmClassNode.actionsName;
80 | fsmName = fsmClassNode.className;
81 |
82 | fsmImplementation += "#include \n";
83 | fsmImplementation += String.format("#include \"%s.h\"\n", toLowerCamelCase(actionsName));
84 | fsmImplementation += String.format("#include \"%s.h\"\n\n", toLowerCamelCase(fsmName));
85 | fsmClassNode.eventEnum.accept(this);
86 | fsmClassNode.stateEnum.accept(this);
87 |
88 | fsmImplementation += String.format("\n" +
89 | "struct %s {\n" +
90 | "\tenum State state;\n" +
91 | "\tstruct %s *actions;\n" +
92 | "};\n\n", fsmName, actionsName);
93 |
94 | fsmClassNode.stateProperty.accept(this);
95 |
96 | for (String action : fsmClassNode.actions) {
97 | fsmImplementation += String.format("" +
98 | "static void %s(struct %s *fsm) {\n" +
99 | "\tfsm->actions->%s();\n" +
100 | "}\n\n", action, fsmName, action);
101 | }
102 | fsmClassNode.handleEvent.accept(this);
103 |
104 | String includeGuard = fsmName.toUpperCase();
105 | fsmHeader += String.format("#ifndef %s_H\n#define %s_H\n\n", includeGuard, includeGuard);
106 | fsmHeader += String.format("struct %s;\n", actionsName);
107 | fsmHeader += String.format("struct %s;\n", fsmName);
108 | fsmHeader += String.format("struct %s *make_%s(struct %s*);\n", fsmName, fsmName, actionsName);
109 | fsmClassNode.delegators.accept(this);
110 | fsmHeader += "#endif\n";
111 | }
112 |
113 | public void visit(HandleEventNode handleEventNode) {
114 | fsmImplementation += String.format("" +
115 | "static void processEvent(enum State state, enum Event event, struct %s *fsm, char *event_name) {\n",
116 | fsmName);
117 | handleEventNode.switchCase.accept(this);
118 | fsmImplementation += "}\n\n";
119 | }
120 |
121 | public void visit(EnumeratorNode enumeratorNode) {
122 | fsmImplementation += enumeratorNode.enumerator;
123 | }
124 |
125 | public void visit(DefaultCaseNode defaultCaseNode) {
126 | fsmImplementation += String.format("" +
127 | "default:\n" +
128 | "(fsm->actions->unexpected_transition)(\"%s\", event_name);\n" +
129 | "break;\n", defaultCaseNode.state);
130 | }
131 |
132 | public String getFsmHeader() {
133 | return fsmHeader;
134 | }
135 |
136 | public String getFsmImplementation() {
137 | return fsmImplementation;
138 | }
139 |
140 | public List getErrors() {
141 | return errors;
142 | }
143 |
144 | public enum Error {NO_ACTION}
145 |
146 | static private String toLowerCamelCase(String s) {
147 | if (s.length() < 2) return s.toLowerCase();
148 | return s.substring(0, 1).toLowerCase() + s.substring(1);
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/src/smc/implementers/CppNestedSwitchCaseImplementer.java:
--------------------------------------------------------------------------------
1 | package smc.implementers;
2 |
3 | import smc.Utilities;
4 | import smc.generators.nestedSwitchCaseGenerator.NSCNodeVisitor;
5 |
6 | import java.util.ArrayList;
7 | import java.util.List;
8 | import java.util.Map;
9 |
10 | import static smc.generators.nestedSwitchCaseGenerator.NSCNode.*;
11 |
12 | public class CppNestedSwitchCaseImplementer implements NSCNodeVisitor {
13 | private String fsmName;
14 | private String actionsName;
15 | private String output = "";
16 | private List errors = new ArrayList<>();
17 | private Map flags;
18 |
19 | public CppNestedSwitchCaseImplementer(Map flags) {
20 | this.flags = flags;
21 | }
22 |
23 | public void visit(SwitchCaseNode switchCaseNode) {
24 | output += String.format("switch (%s) {\n", switchCaseNode.variableName);
25 | switchCaseNode.generateCases(this);
26 | output += "}\n";
27 | }
28 |
29 | public void visit(CaseNode caseNode) {
30 | output += String.format("case %s_%s:\n",caseNode.switchName, caseNode.caseName);
31 | caseNode.caseActionNode.accept(this);
32 | output += "break;\n\n";
33 | }
34 |
35 | public void visit(FunctionCallNode functionCallNode) {
36 | output += String.format("%s(", functionCallNode.functionName);
37 | if (functionCallNode.argument != null) {
38 | functionCallNode.argument.accept(this);
39 | }
40 | output += ");\n";
41 | }
42 |
43 | public void visit(EnumNode enumNode) {
44 | output += String.format(
45 | "\tenum %s {%s};\n",
46 | enumNode.name,
47 | Utilities.commaList(Utilities.addPrefix(enumNode.name + "_", enumNode.enumerators)));
48 | }
49 |
50 | public void visit(StatePropertyNode statePropertyNode) {
51 | output += "State_"+statePropertyNode.initialState;
52 | }
53 |
54 | public void visit(EventDelegatorsNode eventDelegatorsNode) {
55 | for (String event : eventDelegatorsNode.events) {
56 | output += String.format("\tvoid %s() {processEvent(Event_%s, \"%s\");}\n", event, event, event);
57 | }
58 | }
59 |
60 | public void visit(FSMClassNode fsmClassNode) {
61 | if (fsmClassNode.actionsName == null) {
62 | errors.add(Error.NO_ACTIONS);
63 | return;
64 | }
65 |
66 | fsmName = fsmClassNode.className;
67 | String includeGuard = fsmName.toUpperCase();
68 | output += String.format("#ifndef %s_H\n#define %s_H\n\n", includeGuard, includeGuard);
69 |
70 | actionsName = fsmClassNode.actionsName;
71 | output += String.format("#include \"%s.h\"\n", actionsName);
72 |
73 | output += String.format("\n" +
74 | "class %s : public %s {\n" +
75 | "public:\n" +
76 | "\t%s()\n\t: state(", fsmName, actionsName,fsmName);
77 | fsmClassNode.stateProperty.accept(this);
78 | output += ")\n\t{}\n\n";
79 |
80 | fsmClassNode.delegators.accept(this);
81 | output += "\nprivate:\n";
82 | fsmClassNode.stateEnum.accept(this);
83 | output += "\tState state;\n";
84 | output += "\tvoid setState(State s) {state=s;}\n";
85 | fsmClassNode.eventEnum.accept(this);
86 | fsmClassNode.handleEvent.accept(this);
87 |
88 | output += "};\n\n";
89 | output += "#endif\n";
90 | }
91 |
92 | public void visit(HandleEventNode handleEventNode) {
93 | output += "\tvoid processEvent(Event event, const char* eventName) {\n";
94 | handleEventNode.switchCase.accept(this);
95 | output += "}\n\n";
96 | }
97 |
98 | public void visit(EnumeratorNode enumeratorNode) {
99 | output += enumeratorNode.enumeration + "_" + enumeratorNode.enumerator;
100 | }
101 |
102 | public void visit(DefaultCaseNode defaultCaseNode) {
103 | output += String.format("" +
104 | "default:\n" +
105 | "unexpected_transition(\"%s\", eventName);\n" +
106 | "break;\n", defaultCaseNode.state);
107 | }
108 |
109 | public String getOutput() {
110 | return output;
111 | }
112 |
113 | public List getErrors() {
114 | return errors;
115 | }
116 |
117 | public enum Error {NO_ACTIONS}
118 | }
119 |
--------------------------------------------------------------------------------
/src/smc/implementers/DartNestedSwitchCaseImplementer.java:
--------------------------------------------------------------------------------
1 | package smc.implementers;
2 |
3 | import smc.Utilities;
4 | import smc.generators.nestedSwitchCaseGenerator.NSCNodeVisitor;
5 |
6 | import java.util.ArrayList;
7 | import java.util.List;
8 | import java.util.Map;
9 |
10 | import static smc.generators.nestedSwitchCaseGenerator.NSCNode.*;
11 |
12 | public class DartNestedSwitchCaseImplementer implements NSCNodeVisitor {
13 | private String fsmName;
14 | private String actionsName;
15 | private String output = "";
16 | private List errors = new ArrayList<>();
17 | private Map flags;
18 |
19 | public DartNestedSwitchCaseImplementer(Map flags) {
20 | this.flags = flags;
21 | }
22 |
23 | public void visit(SwitchCaseNode switchCaseNode) {
24 | output += String.format("switch (%s) {\n", switchCaseNode.variableName);
25 | switchCaseNode.generateCases(this);
26 | output += "}\n";
27 | }
28 |
29 | public void visit(CaseNode caseNode) {
30 | output += String.format("case %s.%s:\n",caseNode.switchName, caseNode.caseName);
31 | caseNode.caseActionNode.accept(this);
32 | output += "break;\n\n";
33 | }
34 |
35 | public void visit(FunctionCallNode functionCallNode) {
36 | output += String.format("%s(", functionCallNode.functionName);
37 | if (functionCallNode.argument != null) {
38 | functionCallNode.argument.accept(this);
39 | }
40 | output += ");\n";
41 | }
42 |
43 | public void visit(EnumNode enumNode) {
44 | output += String.format(
45 | "\nenum %s {%s}\n\n",
46 | enumNode.name,
47 | Utilities.commaList(enumNode.enumerators));
48 | }
49 |
50 | public void visit(StatePropertyNode statePropertyNode) {
51 | output += "State."+statePropertyNode.initialState;
52 | }
53 |
54 | public void visit(EventDelegatorsNode eventDelegatorsNode) {
55 | for (String event : eventDelegatorsNode.events) {
56 | output += String.format("\t%s() {_processEvent(Event.%s, \"%s\");}\n", event, event, event);
57 | }
58 | }
59 |
60 | public void visit(FSMClassNode fsmClassNode) {
61 | if (fsmClassNode.actionsName == null) {
62 | errors.add(Error.NO_ACTIONS);
63 | return;
64 | }
65 |
66 | fsmName = fsmClassNode.className;
67 | actionsName = fsmClassNode.actionsName;
68 |
69 | output += "import 'package:meta/meta.dart';\n\n";
70 | output += String.format("import '%s.dart';\n", actionsName);
71 |
72 | fsmClassNode.stateEnum.accept(this);
73 | fsmClassNode.eventEnum.accept(this);
74 |
75 | output += String.format("\n" +
76 | "abstract class %s extends %s {\n" +
77 | "\tState state;\n\n" +
78 | "\t%s({@required this.state = ", fsmName, actionsName,fsmName);
79 | fsmClassNode.stateProperty.accept(this);
80 | output += "})\n\t : assert(state != null);\n\n";
81 |
82 | fsmClassNode.delegators.accept(this);
83 | output += "\n\tsetState(State s) {state=s;}\n\n";
84 |
85 | fsmClassNode.handleEvent.accept(this);
86 | output += "}\n";
87 | }
88 |
89 | public void visit(HandleEventNode handleEventNode) {
90 | output += "\t_processEvent(final Event event, final String eventName) {\n";
91 | handleEventNode.switchCase.accept(this);
92 | output += "}\n\n";
93 | }
94 |
95 | public void visit(EnumeratorNode enumeratorNode) {
96 | output += enumeratorNode.enumeration + "." + enumeratorNode.enumerator;
97 | }
98 |
99 | public void visit(DefaultCaseNode defaultCaseNode) {
100 | output += String.format("" +
101 | "default:\n" +
102 | "unexpected_transition(\"%s\", eventName);\n" +
103 | "break;\n", defaultCaseNode.state);
104 | }
105 |
106 | public String getOutput() {
107 | return output;
108 | }
109 |
110 | public List getErrors() {
111 | return errors;
112 | }
113 |
114 | public enum Error {NO_ACTIONS}
115 | }
116 |
--------------------------------------------------------------------------------
/src/smc/implementers/GoNestedSwitchCaseImplementer.java:
--------------------------------------------------------------------------------
1 | package smc.implementers;
2 |
3 | import smc.Utilities;
4 | import smc.generators.nestedSwitchCaseGenerator.NSCNodeVisitor;
5 |
6 | import java.util.ArrayList;
7 | import java.util.List;
8 | import java.util.Map;
9 |
10 | import static smc.generators.nestedSwitchCaseGenerator.NSCNode.*;
11 |
12 | public class GoNestedSwitchCaseImplementer implements NSCNodeVisitor {
13 | private String fsmName;
14 | private String actionsName;
15 | private String output = "";
16 | private List actions = new ArrayList<>();
17 | private List errors = new ArrayList<>();
18 | private List states = new ArrayList<>();
19 | private Map flags;
20 |
21 | public GoNestedSwitchCaseImplementer(Map flags) {
22 | this.flags = flags;
23 | }
24 |
25 | public void visit(SwitchCaseNode switchCaseNode) {
26 | output += String.format("\tswitch %s {\n", switchCaseNode.variableName);
27 | switchCaseNode.generateCases(this);
28 | output += "}\n";
29 | }
30 |
31 | public void visit(CaseNode caseNode) {
32 | output += String.format("\tcase %s%s:\n", caseNode.switchName.toLowerCase(), caseNode.caseName);
33 | caseNode.caseActionNode.accept(this);
34 | output += "\n\n";
35 | }
36 |
37 | public void visit(FunctionCallNode functionCallNode) {
38 | output += String.format("%s(", functionCallNode.functionName);
39 | if (functionCallNode.argument != null) {
40 | functionCallNode.argument.accept(this);
41 | }
42 | output += ")\n";
43 | }
44 |
45 | public void visit(EnumNode enumNode) {
46 | output += String.format(
47 | "const (\n%s)\n\n",
48 | Utilities.iotaList(
49 | enumNode.name.toLowerCase() + "T",
50 | Utilities.addPrefix(enumNode.name.toLowerCase(), enumNode.enumerators)));
51 | }
52 |
53 | public void visit(StatePropertyNode statePropertyNode) {
54 | output += "state"+statePropertyNode.initialState;
55 | }
56 |
57 | public void visit(EventDelegatorsNode eventDelegatorsNode) {
58 | for (String event : eventDelegatorsNode.events) {
59 | output += String.format("func (f *%s) %s() { f.processEvent(event%s, \"%s\") }\n", fsmName, event, event, event);
60 | }
61 | }
62 |
63 | public void visit(FSMClassNode fsmClassNode) {
64 | if (fsmClassNode.actionsName == null) {
65 | errors.add(Error.NO_ACTIONS);
66 | return;
67 | }
68 |
69 | fsmName = fsmClassNode.className;
70 | actionsName = fsmClassNode.actionsName;
71 | actions = fsmClassNode.actions;
72 | states = fsmClassNode.states;
73 |
74 | output += String.format(
75 | "// Package %s is an auto-generated Finite State Machine.\n" +
76 | "// DO NOT EDIT.\n" +
77 | "package %s\n\n" +
78 | "// %s is the Finite State Machine.\n" +
79 | "type %s struct {\n" +
80 | "\tactions %s\n" +
81 | "\tstate stateT\n" +
82 | "}\n\n" +
83 | "// New returns a new %s.\n" +
84 | "func New(actions %s) *%s {\n" +
85 | "\t return &%s{actions: actions, state: ",
86 | fsmName.toLowerCase(), fsmName.toLowerCase(), fsmName, fsmName,
87 | actionsName, fsmName, actionsName, fsmName, fsmName);
88 | fsmClassNode.stateProperty.accept(this);
89 | output += "}\n}\n\n";
90 |
91 | fsmClassNode.delegators.accept(this);
92 |
93 | output += "type stateT int\n";
94 | fsmClassNode.stateEnum.accept(this);
95 | output += "type eventT int\n";
96 | fsmClassNode.eventEnum.accept(this);
97 | fsmClassNode.handleEvent.accept(this);
98 | }
99 |
100 | public void visit(HandleEventNode handleEventNode) {
101 | output += String.format(
102 | "func (f *%s) processEvent(event eventT, eventName string) {\n" +
103 | "\tstate := f.state\n" +
104 | "\tsetState := func(s stateT) { f.state = s; state = s }\n",
105 | fsmName);
106 |
107 | for (String action : actions) {
108 | output += String.format(
109 | "\t%s := func() { f.actions.%s() }\n",
110 | action, Utilities.capitalize(action));
111 | }
112 | output += "\n";
113 |
114 | for (String state : states) {
115 | output += String.format(
116 | "\tconst State%s = state%s\n",
117 | state, Utilities.capitalize(state));
118 | }
119 | output += "\n";
120 |
121 | handleEventNode.switchCase.accept(this);
122 | output += "}\n\n";
123 | }
124 |
125 | public void visit(EnumeratorNode enumeratorNode) {
126 | output += enumeratorNode.enumeration + enumeratorNode.enumerator;
127 | }
128 |
129 | public void visit(DefaultCaseNode defaultCaseNode) {
130 | output += String.format(
131 | "" +
132 | "\tdefault:\n" +
133 | "\t\tf.actions.UnexpectedTransition(\"%s\", eventName);\n\n",
134 | defaultCaseNode.state);
135 | }
136 |
137 | public String getOutput() {
138 | return output;
139 | }
140 |
141 | public List getErrors() {
142 | return errors;
143 | }
144 |
145 | public enum Error {NO_ACTIONS}
146 | }
147 |
--------------------------------------------------------------------------------
/src/smc/implementers/JavaNestedSwitchCaseImplementer.java:
--------------------------------------------------------------------------------
1 | package smc.implementers;
2 |
3 | import smc.Utilities;
4 | import smc.generators.nestedSwitchCaseGenerator.NSCNode;
5 | import smc.generators.nestedSwitchCaseGenerator.NSCNodeVisitor;
6 |
7 | import java.util.Map;
8 |
9 | public class JavaNestedSwitchCaseImplementer implements NSCNodeVisitor {
10 | private String output = "";
11 | private Map flags;
12 | private String javaPackage = null;
13 |
14 | public JavaNestedSwitchCaseImplementer(Map flags) {
15 | this.flags = flags;
16 | if (flags.containsKey("package"))
17 | javaPackage = flags.get("package");
18 | }
19 |
20 | public void visit(NSCNode.SwitchCaseNode switchCaseNode) {
21 | output += String.format("switch(%s) {\n", switchCaseNode.variableName);
22 | switchCaseNode.generateCases(this);
23 | output += "}\n";
24 | }
25 |
26 | public void visit(NSCNode.CaseNode caseNode) {
27 | output += String.format("case %s:\n", caseNode.caseName);
28 | caseNode.caseActionNode.accept(this);
29 | output += "break;\n";
30 | }
31 |
32 | public void visit(NSCNode.FunctionCallNode functionCallNode) {
33 | output += String.format("%s(", functionCallNode.functionName);
34 | if (functionCallNode.argument != null)
35 | functionCallNode.argument.accept(this);
36 | output += ");\n";
37 | }
38 |
39 | public void visit(NSCNode.EnumNode enumNode) {
40 | output += String.format("private enum %s {%s}\n", enumNode.name, Utilities.commaList(enumNode.enumerators));
41 |
42 | }
43 |
44 | public void visit(NSCNode.StatePropertyNode statePropertyNode) {
45 | output += String.format("private State state = State.%s;\n", statePropertyNode.initialState);
46 | output += "private void setState(State s) {state = s;}\n";
47 | }
48 |
49 | public void visit(NSCNode.EventDelegatorsNode eventDelegatorsNode) {
50 | for (String event : eventDelegatorsNode.events)
51 | output += String.format("public void %s() {handleEvent(Event.%s);}\n", event, event);
52 | }
53 |
54 | public void visit(NSCNode.FSMClassNode fsmClassNode) {
55 | if (javaPackage != null)
56 | output += "package " + javaPackage + ";\n";
57 |
58 | String actionsName = fsmClassNode.actionsName;
59 | if (actionsName == null)
60 | output += String.format("public abstract class %s {\n", fsmClassNode.className);
61 | else
62 | output += String.format("public abstract class %s implements %s {\n", fsmClassNode.className, actionsName);
63 |
64 | output += "public abstract void unhandledTransition(String state, String event);\n";
65 | fsmClassNode.stateEnum.accept(this);
66 | fsmClassNode.eventEnum.accept(this);
67 | fsmClassNode.stateProperty.accept(this);
68 | fsmClassNode.delegators.accept(this);
69 | fsmClassNode.handleEvent.accept(this);
70 | if (actionsName == null) {
71 | for (String action : fsmClassNode.actions)
72 | output += String.format("protected abstract void %s();\n", action);
73 | }
74 | output += "}\n";
75 | }
76 |
77 | public void visit(NSCNode.HandleEventNode handleEventNode) {
78 | output += "private void handleEvent(Event event) {\n";
79 | handleEventNode.switchCase.accept(this);
80 | output += "}\n";
81 | }
82 |
83 | public void visit(NSCNode.EnumeratorNode enumeratorNode) {
84 | output += String.format("%s.%s", enumeratorNode.enumeration, enumeratorNode.enumerator);
85 | }
86 |
87 | public void visit(NSCNode.DefaultCaseNode defaultCaseNode) {
88 | output += "default: unhandledTransition(state.name(), event.name()); break;\n";
89 | }
90 |
91 | public String getOutput() {
92 | return output;
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/smc/lexer/Lexer.java:
--------------------------------------------------------------------------------
1 | package smc.lexer;
2 |
3 | import java.util.regex.Matcher;
4 | import java.util.regex.Pattern;
5 |
6 | public class Lexer {
7 | private TokenCollector collector;
8 | private int lineNumber;
9 | private int position;
10 |
11 | public Lexer(TokenCollector collector) {
12 | this.collector = collector;
13 | }
14 |
15 | public void lex(String s) {
16 | lineNumber = 1;
17 | String lines[] = s.split("\n");
18 | for (String line : lines) {
19 | lexLine(line);
20 | lineNumber++;
21 | }
22 | }
23 |
24 | private void lexLine(String line) {
25 | for (position = 0; position < line.length(); )
26 | lexToken(line);
27 | }
28 |
29 | private void lexToken(String line) {
30 | if (!findToken(line)) {
31 | collector.error(lineNumber, position + 1);
32 | position += 1;
33 | }
34 | }
35 |
36 | private boolean findToken(String line) {
37 | return
38 | findWhiteSpace(line) ||
39 | findSingleCharacterToken(line) ||
40 | findName(line);
41 | }
42 |
43 | private static Pattern whitePattern = Pattern.compile("^\\s+");
44 | private static Pattern commentPattern = Pattern.compile("^//.*$");
45 | private static Pattern[] whitePatterns = new Pattern[] {whitePattern, commentPattern};
46 |
47 | private boolean findWhiteSpace(String line) {
48 | for (Pattern pattern : whitePatterns) {
49 | Matcher matcher = pattern.matcher(line.substring(position));
50 | if (matcher.find()) {
51 | position += matcher.end();
52 | return true;
53 | }
54 | }
55 |
56 | return false;
57 | }
58 |
59 | private boolean findSingleCharacterToken(String line) {
60 | String c = line.substring(position, position + 1);
61 | switch (c) {
62 | case "{":
63 | collector.openBrace(lineNumber, position);
64 | break;
65 | case "}":
66 | collector.closedBrace(lineNumber, position);
67 | break;
68 | case "(":
69 | collector.openParen(lineNumber, position);
70 | break;
71 | case ")":
72 | collector.closedParen(lineNumber, position);
73 | break;
74 | case "<":
75 | collector.openAngle(lineNumber, position);
76 | break;
77 | case ">":
78 | collector.closedAngle(lineNumber, position);
79 | break;
80 | case "-":
81 | collector.dash(lineNumber, position);
82 | break;
83 | case "*":
84 | collector.dash(lineNumber, position);
85 | break;
86 | case ":":
87 | collector.colon(lineNumber, position);
88 | break;
89 | default:
90 | return false;
91 | }
92 | position++;
93 | return true;
94 | }
95 |
96 | private static Pattern namePattern = Pattern.compile("^\\w+");
97 |
98 | private boolean findName(String line) {
99 | Matcher nameMatcher = namePattern.matcher(line.substring(position));
100 | if (nameMatcher.find()) {
101 | collector.name(nameMatcher.group(0), lineNumber, position);
102 | position += nameMatcher.end();
103 | return true;
104 | }
105 | return false;
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/smc/lexer/TokenCollector.java:
--------------------------------------------------------------------------------
1 | package smc.lexer;
2 |
3 | public interface TokenCollector {
4 | void openBrace(int line, int pos);
5 | void closedBrace(int line, int pos);
6 | void openParen(int line, int pos);
7 | void closedParen(int line, int pos);
8 | void openAngle(int line, int pos);
9 | void closedAngle(int line, int pos);
10 | void dash(int line, int pos);
11 | void colon(int line, int pos);
12 | void name(String name, int line, int pos);
13 | void error(int line, int pos);
14 | }
15 |
--------------------------------------------------------------------------------
/src/smc/optimizer/Optimizer.java:
--------------------------------------------------------------------------------
1 | package smc.optimizer;
2 |
3 | import smc.OptimizedStateMachine;
4 | import smc.semanticAnalyzer.SemanticStateMachine;
5 |
6 | import java.util.*;
7 |
8 | import static smc.OptimizedStateMachine.*;
9 | import static smc.semanticAnalyzer.SemanticStateMachine.SemanticState;
10 | import static smc.semanticAnalyzer.SemanticStateMachine.SemanticTransition;
11 |
12 | public class Optimizer {
13 | private OptimizedStateMachine optimizedStateMachine;
14 | private SemanticStateMachine semanticStateMachine;
15 |
16 | public OptimizedStateMachine optimize(SemanticStateMachine ast) {
17 | this.semanticStateMachine = ast;
18 | optimizedStateMachine = new OptimizedStateMachine();
19 | addHeader(ast);
20 | addLists();
21 | addTransitions();
22 | return optimizedStateMachine;
23 | }
24 |
25 | private void addTransitions() {
26 | for (SemanticState s : semanticStateMachine.states.values())
27 | if (!s.abstractState)
28 | new StateOptimizer(s).addTransitionsForState();
29 | }
30 |
31 | private class StateOptimizer {
32 | private SemanticState currentState;
33 | private Set eventsForThisState = new HashSet<>();
34 |
35 | public StateOptimizer(SemanticState currentState) {
36 | this.currentState = currentState;
37 | }
38 |
39 | private void addTransitionsForState() {
40 | Transition transition = new Transition();
41 | transition.currentState = currentState.name;
42 | addSubTransitions(transition);
43 | optimizedStateMachine.transitions.add(transition);
44 | }
45 |
46 | private void addSubTransitions(Transition transition) {
47 | for (SemanticState stateInHierarchy : makeRootFirstHierarchyOfStates())
48 | addStateTransitions(transition, stateInHierarchy);
49 | }
50 |
51 | private List makeRootFirstHierarchyOfStates() {
52 | List hierarchy = new ArrayList<>();
53 | addAllStatesInHiearchyLeafFirst(currentState, hierarchy);
54 | Collections.reverse(hierarchy);
55 | return hierarchy;
56 | }
57 |
58 | private void addStateTransitions(Transition transition, SemanticState state) {
59 | for (SemanticTransition semanticTransition : state.transitions) {
60 | if (eventExistsAndHasNotBeenOverridden(semanticTransition.event))
61 | addSubTransition(semanticTransition, transition);
62 | }
63 | }
64 |
65 | private boolean eventExistsAndHasNotBeenOverridden(String event) {
66 | return event != null && !eventsForThisState.contains(event);
67 | }
68 |
69 | private void addSubTransition(SemanticTransition semanticTransition, Transition transition) {
70 | eventsForThisState.add(semanticTransition.event);
71 | SubTransition subTransition = new SubTransition();
72 | new SubTransitionOptimizer(semanticTransition, subTransition).optimize();
73 | transition.subTransitions.add(subTransition);
74 | }
75 |
76 | private class SubTransitionOptimizer {
77 | private SemanticTransition semanticTransition;
78 | private SubTransition subTransition;
79 |
80 | public SubTransitionOptimizer(SemanticTransition semanticTransition, SubTransition subTransition) {
81 | this.semanticTransition = semanticTransition;
82 | this.subTransition = subTransition;
83 | }
84 |
85 | public void optimize() {
86 | subTransition.event = semanticTransition.event;
87 | subTransition.nextState = semanticTransition.nextState.name;
88 | addExitActions(currentState);
89 | addEntryActions(semanticTransition.nextState);
90 | subTransition.actions.addAll(semanticTransition.actions);
91 | }
92 |
93 | private void addEntryActions(SemanticState entryState) {
94 | List hierarchy = new ArrayList<>();
95 | addAllStatesInHiearchyLeafFirst(entryState, hierarchy);
96 | for (SemanticState superState : hierarchy) {
97 | subTransition.actions.addAll(superState.entryActions);
98 | }
99 | }
100 |
101 | private void addExitActions(SemanticState exitState) {
102 | List hierarchy = new ArrayList<>();
103 | addAllStatesInHiearchyLeafFirst(exitState, hierarchy);
104 | Collections.reverse(hierarchy);
105 | for (SemanticState superState : hierarchy) {
106 | subTransition.actions.addAll(superState.exitActions);
107 | }
108 | }
109 | } // SubTransitionOptimizer
110 | } // StateOptimizer
111 |
112 | private void addAllStatesInHiearchyLeafFirst(SemanticState state, List hierarchy) {
113 | for (SemanticState superState : state.superStates) {
114 | if (!hierarchy.contains(superState))
115 | addAllStatesInHiearchyLeafFirst(superState, hierarchy);
116 | }
117 | hierarchy.add(state);
118 | }
119 |
120 | private void addHeader(SemanticStateMachine ast) {
121 | optimizedStateMachine.header = new Header();
122 | optimizedStateMachine.header.fsm = ast.fsmName;
123 | optimizedStateMachine.header.initial = ast.initialState.name;
124 | optimizedStateMachine.header.actions = ast.actionClass;
125 | }
126 |
127 | private void addLists() {
128 | addStates();
129 | addEvents();
130 | addActions();
131 | }
132 |
133 | private void addStates() {
134 | for (SemanticState s : semanticStateMachine.states.values())
135 | if (!s.abstractState)
136 | optimizedStateMachine.states.add(s.name);
137 | }
138 |
139 | private void addEvents() {
140 | optimizedStateMachine.events.addAll(semanticStateMachine.events);
141 | }
142 |
143 | private void addActions() {
144 | optimizedStateMachine.actions.addAll(semanticStateMachine.actions);
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/src/smc/parser/Builder.java:
--------------------------------------------------------------------------------
1 | package smc.parser;
2 |
3 | public interface Builder {
4 | void newHeaderWithName();
5 | void addHeaderWithValue();
6 | void setStateName();
7 | void done();
8 | void setSuperStateName();
9 | void setEvent();
10 | void setNullEvent();
11 | void setEntryAction();
12 | void setExitAction();
13 | void setStateBase();
14 | void setNextState();
15 | void setNullNextState();
16 | void transitionWithAction();
17 | void transitionNullAction();
18 | void addAction();
19 | void transitionWithActions();
20 | void headerError(ParserState state, ParserEvent event, int line, int pos);
21 | void stateSpecError(ParserState state, ParserEvent event, int line, int pos);
22 | void transitionError(ParserState state, ParserEvent event, int line, int pos);
23 | void transitionGroupError(ParserState state, ParserEvent event, int line, int pos);
24 | void endError(ParserState state, ParserEvent event, int line, int pos);
25 | void syntaxError(int line, int pos);
26 | void setName(String name);
27 | }
28 |
--------------------------------------------------------------------------------
/src/smc/parser/FsmSyntax.java:
--------------------------------------------------------------------------------
1 | package smc.parser;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 | import java.util.Objects;
6 |
7 | public class FsmSyntax {
8 | public List headers = new ArrayList<>();
9 | public List logic = new ArrayList<>();
10 | public List errors = new ArrayList<>();
11 | public boolean done = false;
12 |
13 | public static class Header {
14 | public String name;
15 | public String value;
16 |
17 | public static Header NullHeader() {
18 | Header header = new Header(null, null);
19 | return header;
20 | }
21 |
22 | public Header() {
23 | }
24 |
25 | public Header(String name, String value) {
26 | this.name = name;
27 | this.value = value;
28 | }
29 |
30 | public int hashCode() {
31 | return Objects.hash(name, value);
32 | }
33 |
34 | public boolean equals(Object obj) {
35 | if (obj instanceof Header) {
36 | Header other = (Header) obj;
37 | return Objects.equals(other.name, name) && Objects.equals(other.value, value);
38 | }
39 | return false;
40 | }
41 |
42 | }
43 |
44 | public static class Transition {
45 | public StateSpec state;
46 | public List subTransitions = new ArrayList<>();
47 | }
48 |
49 | public static class StateSpec {
50 | public String name;
51 | public List superStates = new ArrayList<>();
52 | public List entryActions = new ArrayList<>();
53 | public List exitActions = new ArrayList<>();
54 | public boolean abstractState;
55 | }
56 |
57 | public static class SubTransition {
58 | public String event;
59 | public String nextState;
60 | public List actions = new ArrayList<>();
61 |
62 | public SubTransition(String event) {
63 | this.event = event;
64 | }
65 | }
66 |
67 | public static class SyntaxError {
68 | public Type type;
69 | public String msg;
70 | public int lineNumber;
71 | public int position;
72 |
73 | public SyntaxError(Type type, String msg, int lineNumber, int position) {
74 | this.type = type;
75 | this.msg = msg;
76 | this.lineNumber = lineNumber;
77 | this.position = position;
78 | }
79 |
80 | public String toString() {
81 | return String.format("Syntax Error Line: %d, Position: %d. (%s) %s", lineNumber, position, type.name(), msg);
82 | }
83 |
84 | public enum Type {HEADER, STATE, TRANSITION, TRANSITION_GROUP, END, SYNTAX}
85 | }
86 |
87 | public String toString() {
88 | return
89 | formatHeaders() +
90 | formatLogic() +
91 | (done ? ".\n" : "") +
92 | formatErrors();
93 | }
94 |
95 | private String formatHeaders() {
96 | String formattedHeaders = "";
97 | for (Header header : headers)
98 | formattedHeaders += formatHeader(header);
99 | return formattedHeaders;
100 | }
101 |
102 | private String formatHeader(Header header) {
103 | return String.format("%s:%s\n", header.name, header.value);
104 | }
105 |
106 | private String formatLogic() {
107 | if (logic.size() > 0)
108 | return String.format("{\n%s}\n", formatTransitions());
109 | else
110 | return "";
111 | }
112 |
113 | private String formatTransitions() {
114 | String transitions = "";
115 | for (Transition transition : logic)
116 | transitions += formatTransition(transition);
117 | return transitions;
118 | }
119 |
120 | private String formatTransition(Transition transition) {
121 | return
122 | String.format(" %s %s\n",
123 | formatStateName(transition.state),
124 | formatSubTransitions(transition));
125 | }
126 |
127 | private String formatStateName(StateSpec stateSpec) {
128 | String stateName = String.format(stateSpec.abstractState ? "(%s)" : "%s", stateSpec.name);
129 | for (String superState : stateSpec.superStates)
130 | stateName += ":" + superState;
131 | for (String entryAction : stateSpec.entryActions)
132 | stateName += " <" + entryAction;
133 | for (String exitAction : stateSpec.exitActions)
134 | stateName += " >" + exitAction;
135 | return stateName;
136 | }
137 |
138 | private String formatSubTransitions(Transition transition) {
139 | if (transition.subTransitions.size() == 1)
140 | return formatSubTransition(transition.subTransitions.get(0));
141 | else {
142 | String formattedSubTransitions = "{\n";
143 |
144 | for (SubTransition subtransition : transition.subTransitions)
145 | formattedSubTransitions += " " + formatSubTransition(subtransition) + "\n";
146 |
147 | return formattedSubTransitions + " }";
148 | }
149 | }
150 |
151 | private String formatSubTransition(SubTransition subtransition) {
152 | return String.format(
153 | "%s %s %s",
154 | subtransition.event,
155 | subtransition.nextState,
156 | formatActions(subtransition));
157 | }
158 |
159 | private String formatActions(SubTransition subtransition) {
160 | if (subtransition.actions.size() == 1)
161 | return subtransition.actions.get(0);
162 | else {
163 | String actions = "{";
164 | boolean first = true;
165 | for (String action : subtransition.actions) {
166 | actions += (first ? "" : " ") + action;
167 | first = false;
168 | }
169 |
170 | return actions + "}";
171 | }
172 | }
173 |
174 | private String formatErrors() {
175 | if (errors.size() > 0)
176 | return formatError(errors.get(0));
177 | else
178 | return "";
179 | }
180 |
181 | private String formatError(SyntaxError error) {
182 | return String.format(
183 | "Syntax error: %s. %s. line %d, position %d.\n",
184 | error.type.toString(),
185 | error.msg,
186 | error.lineNumber,
187 | error.position);
188 | }
189 |
190 | public String getError() {
191 | return formatErrors();
192 | }
193 |
194 | }
195 |
--------------------------------------------------------------------------------
/src/smc/parser/Parser.java:
--------------------------------------------------------------------------------
1 | package smc.parser;
2 |
3 | /*
4 | ::= *
5 | ::= "Actions:" | "FSM:" | "Initial:"
6 |
7 | ::= "{" * "}"
8 |
9 | ::=
10 | | "{" * "}"
11 |
12 | ::=
13 | ::= | "{" * "}" | "-"
14 | ::=
15 | ::= "" | |
16 | ::= ":"
17 | | "<"
18 | | ">"
19 |
20 | ::= | "-"
21 | :: | "-"
22 | ::=
23 | ::=
24 | ::=
25 | */
26 |
27 | import smc.lexer.TokenCollector;
28 |
29 | import java.util.function.Consumer;
30 |
31 | import static smc.parser.ParserEvent.*;
32 | import static smc.parser.ParserState.*;
33 |
34 | public class Parser implements TokenCollector {
35 | private ParserState state = HEADER;
36 | private Builder builder;
37 |
38 | public Parser(Builder builder) {
39 | this.builder = builder;
40 | }
41 |
42 | public void openBrace(int line, int pos) {
43 | handleEvent(OPEN_BRACE, line, pos);
44 | }
45 |
46 | public void closedBrace(int line, int pos) {
47 | handleEvent(CLOSED_BRACE, line, pos);
48 | }
49 |
50 | public void openParen(int line, int pos) {
51 | handleEvent(OPEN_PAREN, line, pos);
52 | }
53 |
54 | public void closedParen(int line, int pos) {
55 | handleEvent(CLOSED_PAREN, line, pos);
56 | }
57 |
58 | public void openAngle(int line, int pos) {
59 | handleEvent(OPEN_ANGLE, line, pos);
60 | }
61 |
62 | public void closedAngle(int line, int pos) {
63 | handleEvent(CLOSED_ANGLE, line, pos);
64 | }
65 |
66 | public void dash(int line, int pos) {
67 | handleEvent(DASH, line, pos);
68 | }
69 |
70 | public void colon(int line, int pos) {
71 | handleEvent(COLON, line, pos);
72 | }
73 |
74 | public void name(String name, int line, int pos) {
75 | builder.setName(name);
76 | handleEvent(NAME, line, pos);
77 | }
78 |
79 | public void error(int line, int pos) {
80 | builder.syntaxError(line, pos);
81 | }
82 |
83 | class Transition {
84 | Transition(ParserState currentState, ParserEvent event,
85 | ParserState newState, Consumer action) {
86 | this.currentState = currentState;
87 | this.event = event;
88 | this.newState = newState;
89 | this.action = action;
90 | }
91 |
92 | public ParserState currentState;
93 | public ParserEvent event;
94 | public ParserState newState;
95 | public Consumer action;
96 | }
97 |
98 | Transition[] transitions = new Transition[]{
99 | new Transition(HEADER, NAME, HEADER_COLON, t -> t.newHeaderWithName()),
100 | new Transition(HEADER, OPEN_BRACE, STATE_SPEC, null),
101 | new Transition(HEADER_COLON, COLON, HEADER_VALUE, null),
102 | new Transition(HEADER_VALUE, NAME, HEADER, t -> t.addHeaderWithValue()),
103 | new Transition(STATE_SPEC, OPEN_PAREN, SUPER_STATE_NAME, null),
104 | new Transition(STATE_SPEC, NAME, STATE_MODIFIER, t -> t.setStateName()),
105 | new Transition(STATE_SPEC, CLOSED_BRACE, END, t -> t.done()),
106 | new Transition(SUPER_STATE_NAME, NAME, SUPER_STATE_CLOSE, t -> t.setSuperStateName()),
107 | new Transition(SUPER_STATE_CLOSE, CLOSED_PAREN, STATE_MODIFIER, null),
108 | new Transition(STATE_MODIFIER, OPEN_ANGLE, ENTRY_ACTION, null),
109 | new Transition(STATE_MODIFIER, CLOSED_ANGLE, EXIT_ACTION, null),
110 | new Transition(STATE_MODIFIER, COLON, STATE_BASE, null),
111 | new Transition(STATE_MODIFIER, NAME, SINGLE_EVENT, t -> t.setEvent()),
112 | new Transition(STATE_MODIFIER, DASH, SINGLE_EVENT, t -> t.setNullEvent()),
113 | new Transition(STATE_MODIFIER, OPEN_BRACE, SUBTRANSITION_GROUP, null),
114 | new Transition(ENTRY_ACTION, NAME, STATE_MODIFIER, t -> t.setEntryAction()),
115 | new Transition(ENTRY_ACTION, OPEN_BRACE, MULTIPLE_ENTRY_ACTIONS, null),
116 | new Transition(MULTIPLE_ENTRY_ACTIONS, NAME, MULTIPLE_ENTRY_ACTIONS, t -> t.setEntryAction()),
117 | new Transition(MULTIPLE_ENTRY_ACTIONS, CLOSED_BRACE, STATE_MODIFIER, null),
118 | new Transition(EXIT_ACTION, NAME, STATE_MODIFIER, t -> t.setExitAction()),
119 | new Transition(EXIT_ACTION, OPEN_BRACE, MULTIPLE_EXIT_ACTIONS, null),
120 | new Transition(MULTIPLE_EXIT_ACTIONS, NAME, MULTIPLE_EXIT_ACTIONS, t -> t.setExitAction()),
121 | new Transition(MULTIPLE_EXIT_ACTIONS, CLOSED_BRACE, STATE_MODIFIER, null),
122 | new Transition(STATE_BASE, NAME, STATE_MODIFIER, t -> t.setStateBase()),
123 | new Transition(SINGLE_EVENT, NAME, SINGLE_NEXT_STATE, t -> t.setNextState()),
124 | new Transition(SINGLE_EVENT, DASH, SINGLE_NEXT_STATE, t -> t.setNullNextState()),
125 | new Transition(SINGLE_NEXT_STATE, NAME, STATE_SPEC, t -> t.transitionWithAction()),
126 | new Transition(SINGLE_NEXT_STATE, DASH, STATE_SPEC, t -> t.transitionNullAction()),
127 | new Transition(SINGLE_NEXT_STATE, OPEN_BRACE, SINGLE_ACTION_GROUP, null),
128 | new Transition(SINGLE_ACTION_GROUP, NAME, SINGLE_ACTION_GROUP_NAME, t -> t.addAction()),
129 | new Transition(SINGLE_ACTION_GROUP, CLOSED_BRACE, STATE_SPEC, t -> t.transitionNullAction()),
130 | new Transition(SINGLE_ACTION_GROUP_NAME, NAME, SINGLE_ACTION_GROUP_NAME, t -> t.addAction()),
131 | new Transition(SINGLE_ACTION_GROUP_NAME, CLOSED_BRACE, STATE_SPEC, t -> t.transitionWithActions()),
132 | new Transition(SUBTRANSITION_GROUP, CLOSED_BRACE, STATE_SPEC, null),
133 | new Transition(SUBTRANSITION_GROUP, NAME, GROUP_EVENT, t -> t.setEvent()),
134 | new Transition(SUBTRANSITION_GROUP, DASH, GROUP_EVENT, t -> t.setNullEvent()),
135 | new Transition(GROUP_EVENT, NAME, GROUP_NEXT_STATE, t -> t.setNextState()),
136 | new Transition(GROUP_EVENT, DASH, GROUP_NEXT_STATE, t -> t.setNullNextState()),
137 | new Transition(GROUP_NEXT_STATE, NAME, SUBTRANSITION_GROUP, t -> t.transitionWithAction()),
138 | new Transition(GROUP_NEXT_STATE, DASH, SUBTRANSITION_GROUP, t -> t.transitionNullAction()),
139 | new Transition(GROUP_NEXT_STATE, OPEN_BRACE, GROUP_ACTION_GROUP, null),
140 | new Transition(GROUP_ACTION_GROUP, NAME, GROUP_ACTION_GROUP_NAME, t -> t.addAction()),
141 | new Transition(GROUP_ACTION_GROUP, CLOSED_BRACE, SUBTRANSITION_GROUP, t -> t.transitionNullAction()),
142 | new Transition(GROUP_ACTION_GROUP_NAME, NAME, GROUP_ACTION_GROUP_NAME, t -> t.addAction()),
143 | new Transition(GROUP_ACTION_GROUP_NAME, CLOSED_BRACE, SUBTRANSITION_GROUP, t -> t.transitionWithActions()),
144 | new Transition(END, ParserEvent.EOF, END, null)
145 | };
146 |
147 | public void handleEvent(ParserEvent event, int line, int pos) {
148 | for (Transition t : transitions) {
149 | if (t.currentState == state && t.event == event) {
150 | state = t.newState;
151 | if (t.action != null)
152 | t.action.accept(builder);
153 | return;
154 | }
155 | }
156 | handleEventError(event, line, pos);
157 | }
158 |
159 | private void handleEventError(ParserEvent event, int line, int pos) {
160 | switch (state) {
161 | case HEADER:
162 | case HEADER_COLON:
163 | case HEADER_VALUE:
164 | builder.headerError(state, event, line, pos);
165 | break;
166 |
167 | case STATE_SPEC:
168 | case SUPER_STATE_NAME:
169 | case SUPER_STATE_CLOSE:
170 | case STATE_MODIFIER:
171 | case EXIT_ACTION:
172 | case ENTRY_ACTION:
173 | case STATE_BASE:
174 | builder.stateSpecError(state, event, line, pos);
175 | break;
176 |
177 | case SINGLE_EVENT:
178 | case SINGLE_NEXT_STATE:
179 | case SINGLE_ACTION_GROUP:
180 | case SINGLE_ACTION_GROUP_NAME:
181 | builder.transitionError(state, event, line, pos);
182 | break;
183 |
184 | case SUBTRANSITION_GROUP:
185 | case GROUP_EVENT:
186 | case GROUP_NEXT_STATE:
187 | case GROUP_ACTION_GROUP:
188 | case GROUP_ACTION_GROUP_NAME:
189 | builder.transitionGroupError(state, event, line, pos);
190 | break;
191 |
192 | case END:
193 | builder.endError(state, event, line, pos);
194 | break;
195 | }
196 | }
197 | }
198 |
--------------------------------------------------------------------------------
/src/smc/parser/ParserEvent.java:
--------------------------------------------------------------------------------
1 | package smc.parser;
2 |
3 | public enum ParserEvent {
4 | NAME,
5 | OPEN_BRACE,
6 | CLOSED_BRACE,
7 | OPEN_PAREN,
8 | CLOSED_PAREN,
9 | OPEN_ANGLE,
10 | CLOSED_ANGLE,
11 | DASH,
12 | EOF,
13 | COLON
14 | }
15 |
--------------------------------------------------------------------------------
/src/smc/parser/ParserState.java:
--------------------------------------------------------------------------------
1 | package smc.parser;
2 |
3 | public enum ParserState {
4 | HEADER,
5 | HEADER_COLON,
6 | HEADER_VALUE,
7 | STATE_SPEC,
8 | SUPER_STATE_NAME,
9 | SUPER_STATE_CLOSE,
10 | STATE_MODIFIER,
11 | EXIT_ACTION,
12 | ENTRY_ACTION,
13 | STATE_BASE,
14 | SINGLE_EVENT,
15 | SINGLE_NEXT_STATE,
16 | SINGLE_ACTION_GROUP,
17 | SINGLE_ACTION_GROUP_NAME,
18 | SUBTRANSITION_GROUP,
19 | GROUP_EVENT,
20 | GROUP_NEXT_STATE,
21 | GROUP_ACTION_GROUP,
22 | GROUP_ACTION_GROUP_NAME,
23 | MULTIPLE_ENTRY_ACTIONS,
24 | MULTIPLE_EXIT_ACTIONS,
25 | END
26 | }
27 |
--------------------------------------------------------------------------------
/src/smc/parser/SyntaxBuilder.java:
--------------------------------------------------------------------------------
1 | package smc.parser;
2 |
3 | import static smc.parser.FsmSyntax.*;
4 | import static smc.parser.FsmSyntax.SyntaxError.Type.*;
5 |
6 | public class SyntaxBuilder implements Builder {
7 | private FsmSyntax fsm;
8 | private Header header;
9 | private String parsedName;
10 | private Transition transition;
11 | private SubTransition subtransition;
12 |
13 | public SyntaxBuilder() {
14 | fsm = new FsmSyntax();
15 | }
16 |
17 | public void newHeaderWithName() {
18 | header = new Header();
19 | header.name = parsedName;
20 | }
21 |
22 | public void addHeaderWithValue() {
23 | header.value = parsedName;
24 | fsm.headers.add(header);
25 | }
26 |
27 | public void setStateName() {
28 | transition = new Transition();
29 | fsm.logic.add(transition);
30 | transition.state = new StateSpec();
31 | transition.state.name = parsedName;
32 | }
33 |
34 | public void done() {
35 | fsm.done = true;
36 | }
37 |
38 | public void setSuperStateName() {
39 | setStateName();
40 | transition.state.abstractState = true;
41 | }
42 |
43 | public void setEvent() {
44 | subtransition = new SubTransition(parsedName);
45 | }
46 |
47 | public void setNullEvent() {
48 | subtransition = new SubTransition(null);
49 | }
50 |
51 | public void setEntryAction() {
52 | transition.state.entryActions.add(parsedName);
53 | }
54 |
55 | public void setExitAction() {
56 | transition.state.exitActions.add(parsedName);
57 | }
58 |
59 | public void setStateBase() {
60 | transition.state.superStates.add(parsedName);
61 | }
62 |
63 | public void setNextState() {
64 | subtransition.nextState = parsedName;
65 | }
66 |
67 | public void setNullNextState() {
68 | subtransition.nextState = null;
69 | }
70 |
71 | public void transitionWithAction() {
72 | subtransition.actions.add(parsedName);
73 | transition.subTransitions.add(subtransition);
74 | }
75 |
76 | public void transitionNullAction() {
77 | transition.subTransitions.add(subtransition);
78 | }
79 |
80 | public void addAction() {
81 | subtransition.actions.add(parsedName);
82 | }
83 |
84 | public void transitionWithActions() {
85 | transition.subTransitions.add(subtransition);
86 | }
87 |
88 | public void headerError(ParserState state, ParserEvent event, int line, int pos) {
89 | fsm.errors.add(new SyntaxError(HEADER, state+"|"+event, line, pos));
90 | }
91 |
92 | public void stateSpecError(ParserState state, ParserEvent event, int line, int pos) {
93 | fsm.errors.add(new SyntaxError(STATE, state+"|"+event, line, pos));
94 |
95 | }
96 |
97 | public void transitionError(ParserState state, ParserEvent event, int line, int pos) {
98 | fsm.errors.add(new SyntaxError(TRANSITION, state+"|"+event, line, pos));
99 | }
100 |
101 | public void transitionGroupError(ParserState state, ParserEvent event, int line, int pos) {
102 | fsm.errors.add(new SyntaxError(TRANSITION_GROUP, state+"|"+event, line, pos));
103 | }
104 |
105 | public void endError(ParserState state, ParserEvent event, int line, int pos) {
106 | fsm.errors.add(new SyntaxError(END, state+"|"+event, line, pos));
107 | }
108 |
109 | public void syntaxError(int line, int pos) {
110 | fsm.errors.add(new SyntaxError(SYNTAX, "", line, pos));
111 | }
112 |
113 | public void setName(String name) {
114 | parsedName = name;
115 | }
116 |
117 | public FsmSyntax getFsm() {
118 | return fsm;
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/src/smc/semanticAnalyzer/SemanticAnalyzer.java:
--------------------------------------------------------------------------------
1 | package smc.semanticAnalyzer;
2 |
3 | import smc.parser.FsmSyntax;
4 |
5 | import java.util.*;
6 |
7 | import static smc.parser.FsmSyntax.*;
8 | import static smc.semanticAnalyzer.SemanticStateMachine.*;
9 | import static smc.semanticAnalyzer.SemanticStateMachine.AnalysisError.ID.*;
10 |
11 | public class SemanticAnalyzer {
12 | private SemanticStateMachine semanticStateMachine;
13 | private Header fsmHeader = Header.NullHeader();
14 | private Header actionsHeader = new Header();
15 | private Header initialHeader = new Header();
16 |
17 | public SemanticStateMachine analyze(FsmSyntax fsm) {
18 | semanticStateMachine = new SemanticStateMachine();
19 | analyzeHeaders(fsm);
20 | checkSemanticValidity(fsm);
21 | produceSemanticStateMachine(fsm);
22 | return semanticStateMachine;
23 | }
24 |
25 | private void analyzeHeaders(FsmSyntax fsm) {
26 | setHeaders(fsm);
27 | checkMissingHeaders();
28 | }
29 |
30 | private void setHeaders(FsmSyntax fsm) {
31 | for (Header header : fsm.headers) {
32 | if (isNamed(header, "fsm"))
33 | setHeader(fsmHeader, header);
34 | else if (isNamed(header, "actions"))
35 | setHeader(actionsHeader, header);
36 | else if (isNamed(header, "initial"))
37 | setHeader(initialHeader, header);
38 | else
39 | semanticStateMachine.addError(new AnalysisError(INVALID_HEADER, header));
40 | }
41 | }
42 |
43 | private boolean isNamed(Header header, String headerName) {
44 | return header.name.equalsIgnoreCase(headerName);
45 | }
46 |
47 | private void setHeader(Header targetHeader, Header header) {
48 | if (isNullHeader(targetHeader)) {
49 | targetHeader.name = header.name;
50 | targetHeader.value = header.value;
51 | } else
52 | semanticStateMachine.addError(new AnalysisError(EXTRA_HEADER_IGNORED, header));
53 | }
54 |
55 | private void checkMissingHeaders() {
56 | if (isNullHeader(fsmHeader))
57 | semanticStateMachine.addError(new AnalysisError(AnalysisError.ID.NO_FSM));
58 | if (isNullHeader(initialHeader))
59 | semanticStateMachine.addError(new AnalysisError(AnalysisError.ID.NO_INITIAL));
60 | }
61 |
62 | private boolean isNullHeader(Header header) {
63 | return header.name == null;
64 | }
65 |
66 | private void checkSemanticValidity(FsmSyntax fsm) {
67 | createStateEventAndActionLists(fsm);
68 | checkUndefinedStates(fsm);
69 | checkForUnusedStates(fsm);
70 | checkForDuplicateTransitions(fsm);
71 | checkThatAbstractStatesAreNotTargets(fsm);
72 | checkForInconsistentAbstraction(fsm);
73 | checkForMultiplyDefinedStateActions(fsm);
74 | }
75 |
76 | private void createStateEventAndActionLists(FsmSyntax fsm) {
77 | addStateNamesToStateList(fsm);
78 | addEntryAndExitActionsToActionList(fsm);
79 | addEventsToEventList(fsm);
80 | addTransitionActionsToActionList(fsm);
81 | }
82 |
83 | private void addTransitionActionsToActionList(FsmSyntax fsm) {
84 | for (Transition t : fsm.logic)
85 | for (SubTransition st : t.subTransitions)
86 | for (String action : st.actions)
87 | semanticStateMachine.actions.add(action);
88 | }
89 |
90 | private void addEventsToEventList(FsmSyntax fsm) {
91 | for (Transition t : fsm.logic)
92 | for (SubTransition st : t.subTransitions)
93 | if (st.event != null)
94 | semanticStateMachine.events.add(st.event);
95 | }
96 |
97 | private void addEntryAndExitActionsToActionList(FsmSyntax fsm) {
98 | for (Transition t : fsm.logic) {
99 | for (String entryAction : t.state.entryActions)
100 | semanticStateMachine.actions.add(entryAction);
101 | for (String exitAction : t.state.exitActions)
102 | semanticStateMachine.actions.add(exitAction);
103 | }
104 | }
105 |
106 | private void addStateNamesToStateList(FsmSyntax fsm) {
107 | for (Transition t : fsm.logic) {
108 | SemanticState state = new SemanticState(t.state.name);
109 | semanticStateMachine.states.put(state.name, state);
110 | }
111 | }
112 |
113 | private void checkUndefinedStates(FsmSyntax fsm) {
114 | for (Transition t : fsm.logic) {
115 | for (String superState : t.state.superStates)
116 | checkUndefinedState(superState, UNDEFINED_SUPER_STATE);
117 |
118 | for (SubTransition st : t.subTransitions)
119 | checkUndefinedState(st.nextState, UNDEFINED_STATE);
120 | }
121 |
122 | if (initialHeader.value != null && !semanticStateMachine.states.containsKey(initialHeader.value))
123 | semanticStateMachine.errors.add(new AnalysisError(UNDEFINED_STATE, "initial: " + initialHeader.value));
124 | }
125 |
126 | private void checkForUnusedStates(FsmSyntax fsm) {
127 | findStatesDefinedButNotUsed(findUsedStates(fsm));
128 | }
129 |
130 | private Set findUsedStates(FsmSyntax fsm) {
131 | Set usedStates = new HashSet<>();
132 | usedStates.add(initialHeader.value);
133 | usedStates.addAll(getSuperStates(fsm));
134 | usedStates.addAll(getNextStates(fsm));
135 | return usedStates;
136 | }
137 |
138 | private Set getNextStates(FsmSyntax fsm) {
139 | Set nextStates = new HashSet<>();
140 | for (Transition t : fsm.logic)
141 | for (SubTransition st : t.subTransitions)
142 | if (st.nextState == null) // implicit use of current state.
143 | nextStates.add(t.state.name);
144 | else
145 | nextStates.add(st.nextState);
146 | return nextStates;
147 | }
148 |
149 | private Set getSuperStates(FsmSyntax fsm) {
150 | Set superStates = new HashSet<>();
151 | for (Transition t : fsm.logic)
152 | for (String superState : t.state.superStates)
153 | superStates.add(superState);
154 | return superStates;
155 | }
156 |
157 | private void findStatesDefinedButNotUsed(Set usedStates) {
158 | for (String definedState : semanticStateMachine.states.keySet())
159 | if (!usedStates.contains(definedState))
160 | semanticStateMachine.errors.add(new AnalysisError(UNUSED_STATE, definedState));
161 | }
162 |
163 | private void checkForDuplicateTransitions(FsmSyntax fsm) {
164 | Set transitionKeys = new HashSet<>();
165 | for (Transition t : fsm.logic) {
166 | for (SubTransition st : t.subTransitions) {
167 | String key = String.format("%s(%s)", t.state.name, st.event);
168 | if (transitionKeys.contains(key))
169 | semanticStateMachine.errors.add(new AnalysisError(DUPLICATE_TRANSITION, key));
170 | else
171 | transitionKeys.add(key);
172 | }
173 | }
174 | }
175 |
176 | private void checkThatAbstractStatesAreNotTargets(FsmSyntax fsm) {
177 | Set abstractStates = findAbstractStates(fsm);
178 | for (Transition t : fsm.logic)
179 | for (SubTransition st : t.subTransitions)
180 | if (abstractStates.contains(st.nextState))
181 | semanticStateMachine.errors.add(
182 | new AnalysisError(
183 | ABSTRACT_STATE_USED_AS_NEXT_STATE,
184 | String.format("%s(%s)->%s", t.state.name, st.event, st.nextState)));
185 | }
186 |
187 | private Set findAbstractStates(FsmSyntax fsm) {
188 | Set abstractStates = new HashSet<>();
189 | for (Transition t : fsm.logic)
190 | if (t.state.abstractState)
191 | abstractStates.add(t.state.name);
192 | return abstractStates;
193 | }
194 |
195 | private void checkForInconsistentAbstraction(FsmSyntax fsm) {
196 | Set abstractStates = findAbstractStates(fsm);
197 | for (Transition t : fsm.logic)
198 | if (!t.state.abstractState && abstractStates.contains(t.state.name))
199 | semanticStateMachine.warnings.add(new AnalysisError(INCONSISTENT_ABSTRACTION, t.state.name));
200 | }
201 |
202 | private void checkForMultiplyDefinedStateActions(FsmSyntax fsm) {
203 | Map firstActionsForState = new HashMap<>();
204 | for (Transition t : fsm.logic) {
205 | if (specifiesStateActions(t)) {
206 | String actionsKey = makeActionsKey(t);
207 | if (firstActionsForState.containsKey(t.state.name)) {
208 | if (!firstActionsForState.get(t.state.name).equals(actionsKey))
209 | semanticStateMachine.errors.add(new AnalysisError(STATE_ACTIONS_MULTIPLY_DEFINED, t.state.name));
210 | } else
211 | firstActionsForState.put(t.state.name, actionsKey);
212 | }
213 | }
214 | }
215 |
216 | private boolean specifiesStateActions(Transition t) {
217 | return t.state.entryActions.size() != 0 || t.state.exitActions.size() != 0;
218 | }
219 |
220 | private String makeActionsKey(Transition t) {
221 | List actions = new ArrayList<>();
222 | actions.addAll(t.state.entryActions);
223 | actions.addAll(t.state.exitActions);
224 | return commaList(actions);
225 | }
226 |
227 | private String commaList(List list) {
228 | String commaList = "";
229 | if (list.size() == 0)
230 | return "";
231 | for (String s : list)
232 | commaList += s + ",";
233 | return commaList.substring(0, commaList.length() - 1);
234 |
235 | }
236 |
237 | private void checkUndefinedState(String referencedState, AnalysisError.ID errorCode) {
238 | if (referencedState != null && !semanticStateMachine.states.containsKey(referencedState)) {
239 | semanticStateMachine.errors.add(new AnalysisError(errorCode, referencedState));
240 | }
241 | }
242 |
243 | private void produceSemanticStateMachine(FsmSyntax fsm) {
244 | if (semanticStateMachine.errors.size() == 0) {
245 | compileHeaders();
246 | for (Transition t : fsm.logic) {
247 | SemanticState state = compileState(t);
248 | compileTransitions(t, state);
249 | }
250 |
251 | new SuperClassCrawler().checkSuperClassTransitions();
252 | }
253 | }
254 |
255 | private void compileHeaders() {
256 | semanticStateMachine.initialState = semanticStateMachine.states.get(initialHeader.value);
257 | semanticStateMachine.actionClass = actionsHeader.value;
258 | semanticStateMachine.fsmName = fsmHeader.value;
259 | }
260 |
261 | private SemanticState compileState(Transition t) {
262 | SemanticState state = semanticStateMachine.states.get(t.state.name);
263 | state.entryActions.addAll(t.state.entryActions);
264 | state.exitActions.addAll(t.state.exitActions);
265 | state.abstractState |= t.state.abstractState;
266 | for (String superStateName : t.state.superStates)
267 | state.superStates.add(semanticStateMachine.states.get(superStateName));
268 | return state;
269 | }
270 |
271 | private void compileTransitions(Transition t, SemanticState state) {
272 | for (SubTransition st : t.subTransitions)
273 | compileTransition(state, st);
274 | }
275 |
276 | private void compileTransition(SemanticState state, SubTransition st) {
277 | SemanticTransition semanticTransition = new SemanticTransition();
278 | semanticTransition.event = st.event;
279 | semanticTransition.nextState = st.nextState == null ? state : semanticStateMachine.states.get(st.nextState);
280 | semanticTransition.actions.addAll(st.actions);
281 | state.transitions.add(semanticTransition);
282 | }
283 |
284 | private class SuperClassCrawler {
285 | class TransitionTuple {
286 | TransitionTuple(String currentState, String event, String nextState, List actions) {
287 | this.currentState = currentState;
288 | this.event = event;
289 | this.nextState = nextState;
290 | this.actions = actions;
291 | }
292 |
293 | public int hashCode() {
294 | return Objects.hash(currentState, event, nextState, actions);
295 | }
296 |
297 | public boolean equals(Object obj) {
298 | if (obj instanceof TransitionTuple) {
299 | TransitionTuple tt = (TransitionTuple) obj;
300 | return
301 | Objects.equals(currentState, tt.currentState) &&
302 | Objects.equals(event, tt.event) &&
303 | Objects.equals(nextState, tt.nextState) &&
304 | Objects.equals(actions, tt.actions);
305 | }
306 | return false;
307 | }
308 |
309 | String currentState;
310 | String event;
311 | String nextState;
312 | List actions;
313 | }
314 |
315 | private SemanticState concreteState = null;
316 | private Map transitionTuples;
317 |
318 | private void checkSuperClassTransitions() {
319 | for (SemanticState state : semanticStateMachine.states.values()) {
320 | if (state.abstractState == false) {
321 | concreteState = state;
322 | transitionTuples = new HashMap<>();
323 | checkTransitionsForState(concreteState);
324 | }
325 | }
326 | }
327 |
328 | private void checkTransitionsForState(SemanticState state) {
329 | for (SemanticState superState : state.superStates)
330 | checkTransitionsForState(superState);
331 | checkStateForPreviouslyDefinedTransition(state);
332 | }
333 |
334 | private void checkStateForPreviouslyDefinedTransition(SemanticState state) {
335 | for (SemanticTransition st : state.transitions)
336 | checkTransitionForPreviousDefinition(state, st);
337 | }
338 |
339 | private void checkTransitionForPreviousDefinition(SemanticState state, SemanticTransition st) {
340 | TransitionTuple thisTuple = new TransitionTuple(state.name, st.event, st.nextState.name, st.actions);
341 | if (transitionTuples.containsKey(thisTuple.event)) {
342 | determineIfThePreviousDefinitionIsAnError(state, thisTuple);
343 | } else
344 | transitionTuples.put(thisTuple.event, thisTuple);
345 | }
346 |
347 | private void determineIfThePreviousDefinitionIsAnError(SemanticState state, TransitionTuple thisTuple) {
348 | TransitionTuple previousTuple = transitionTuples.get(thisTuple.event);
349 | if (!transitionsHaveSameOutcomes(thisTuple, previousTuple))
350 | checkForOverriddenTransition(state, thisTuple, previousTuple);
351 | }
352 |
353 | private void checkForOverriddenTransition(SemanticState state, TransitionTuple thisTuple, TransitionTuple previousTuple) {
354 | SemanticState definingState = semanticStateMachine.states.get(previousTuple.currentState);
355 | if (!isSuperStateOf(definingState, state)) {
356 | semanticStateMachine.errors.add(new AnalysisError(CONFLICTING_SUPERSTATES, concreteState.name + "|" + thisTuple.event));
357 | } else
358 | transitionTuples.put(thisTuple.event, thisTuple);
359 | }
360 |
361 | private boolean transitionsHaveSameOutcomes(TransitionTuple t1, TransitionTuple t2) {
362 | return
363 | Objects.equals(t1.nextState, t2.nextState) &&
364 | Objects.equals(t1.actions, t2.actions);
365 | }
366 | }
367 |
368 | private boolean isSuperStateOf(SemanticState possibleSuperState, SemanticState state) {
369 | if (state == possibleSuperState)
370 | return true;
371 | for (SemanticState superState : state.superStates)
372 | if (isSuperStateOf(possibleSuperState, superState))
373 | return true;
374 | return false;
375 | }
376 | }
377 |
--------------------------------------------------------------------------------
/src/smc/semanticAnalyzer/SemanticStateMachine.java:
--------------------------------------------------------------------------------
1 | package smc.semanticAnalyzer;
2 |
3 | import java.util.*;
4 |
5 | public class SemanticStateMachine {
6 | public List errors = new ArrayList<>();
7 | public List warnings = new ArrayList<>();
8 | public SortedMap states = new TreeMap<>();
9 | public Set events = new HashSet<>();
10 | public Set actions = new HashSet<>();
11 | public SemanticState initialState;
12 | public String actionClass;
13 | public String fsmName;
14 |
15 | public String toString() {
16 | return String.format(
17 | "" +
18 | "Actions: %s\n" +
19 | "FSM: %s\n" +
20 | "Initial: %s" +
21 | "%s",
22 | actionClass, fsmName, initialState.name, statesToString());
23 |
24 | }
25 |
26 | public void addError(AnalysisError analysisError) {
27 | errors.add(analysisError);
28 | }
29 |
30 | public String statesToString() {
31 | String statesString = "{";
32 | for (SemanticState s : states.values()) {
33 | statesString += s.toString();
34 | }
35 | return statesString + "}\n";
36 | }
37 |
38 | public static class SemanticState implements Comparable {
39 | public String name;
40 | public List entryActions = new ArrayList<>();
41 | public List exitActions = new ArrayList<>();
42 | public boolean abstractState = false;
43 | public SortedSet superStates = new TreeSet<>();
44 | public List transitions = new ArrayList<>();
45 |
46 | public SemanticState(String name) {
47 | this.name = name;
48 | }
49 |
50 | public boolean equals(Object obj) {
51 | if (obj instanceof SemanticState) {
52 | SemanticState other = (SemanticState) obj;
53 | return
54 | Objects.equals(other.name, name) &&
55 | Objects.equals(other.entryActions, entryActions) &&
56 | Objects.equals(other.exitActions, exitActions) &&
57 | Objects.equals(other.superStates, superStates) &&
58 | Objects.equals(other.transitions, transitions) &&
59 | other.abstractState == abstractState;
60 | } else
61 | return false;
62 | }
63 |
64 | public String toString() {
65 | return
66 | String.format("\n %s {\n%s }\n",
67 | makeStateNameWithAdornments(),
68 | makeTransitionStrings());
69 | }
70 |
71 | private String makeTransitionStrings() {
72 | String transitionStrings = "";
73 | for (SemanticTransition st : transitions)
74 | transitionStrings += makeTransitionString(st);
75 |
76 | return transitionStrings;
77 | }
78 |
79 | private String makeTransitionString(SemanticTransition st) {
80 | return String.format(" %s %s {%s}\n", st.event, makeNextStateName(st), makeActions(st));
81 | }
82 |
83 | private String makeActions(SemanticTransition st) {
84 | String actions = "";
85 | boolean firstAction = true;
86 | for (String action : st.actions) {
87 | actions += (firstAction ? "" : " ") + action;
88 | firstAction = false;
89 | }
90 | return actions;
91 | }
92 |
93 | private String makeNextStateName(SemanticTransition st) {
94 | return st.nextState == null ? "null" : st.nextState.name;
95 | }
96 |
97 | private String makeStateNameWithAdornments() {
98 | String stateName = "";
99 | stateName += abstractState ? ("(" + name + ")") : name;
100 | for (SemanticState superState : superStates)
101 | stateName += " :" + superState.name;
102 | for (String entryAction : entryActions)
103 | stateName += " <" + entryAction;
104 | for (String exitAction : exitActions)
105 | stateName += " >" + exitAction;
106 | return stateName;
107 | }
108 |
109 | public int compareTo(SemanticState s) {
110 | return name.compareTo(s.name);
111 | }
112 | }
113 |
114 | public static class AnalysisError {
115 | public enum ID {
116 | NO_FSM,
117 | NO_INITIAL,
118 | INVALID_HEADER,
119 | EXTRA_HEADER_IGNORED,
120 | UNDEFINED_STATE,
121 | UNDEFINED_SUPER_STATE,
122 | UNUSED_STATE,
123 | DUPLICATE_TRANSITION,
124 | ABSTRACT_STATE_USED_AS_NEXT_STATE,
125 | INCONSISTENT_ABSTRACTION,
126 | STATE_ACTIONS_MULTIPLY_DEFINED,
127 | CONFLICTING_SUPERSTATES,
128 | }
129 |
130 | private ID id;
131 | private Object extra;
132 |
133 | public AnalysisError(ID id) {
134 | this.id = id;
135 | }
136 |
137 | public AnalysisError(ID id, Object extra) {
138 | this(id);
139 | this.extra = extra;
140 | }
141 |
142 | public String toString() {
143 | return String.format("Semantic Error: %s(%s)", id.name(), extra);
144 | }
145 |
146 | public int hashCode() {
147 | return Objects.hash(id, extra);
148 | }
149 |
150 | public boolean equals(Object obj) {
151 | if (obj instanceof AnalysisError) {
152 | AnalysisError other = (AnalysisError) obj;
153 | return id == other.id && Objects.equals(extra, other.extra);
154 | }
155 | return false;
156 | }
157 | }
158 |
159 | public static class SemanticTransition {
160 | public String event;
161 | public SemanticState nextState;
162 | public List actions = new ArrayList<>();
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/test/com/cleancoder/args/ArgsExceptionTest.java:
--------------------------------------------------------------------------------
1 | package com.cleancoder.args;
2 |
3 | import static com.cleancoder.args.ArgsException.ErrorCode.*;
4 |
5 | import junit.framework.TestCase;
6 |
7 | public class ArgsExceptionTest extends TestCase {
8 | public void testUnexpectedMessage() throws Exception {
9 | ArgsException e = new ArgsException(UNEXPECTED_ARGUMENT, 'x', null);
10 | assertEquals("Argument -x unexpected.", e.errorMessage());
11 | }
12 |
13 | public void testMissingStringMessage() throws Exception {
14 | ArgsException e = new ArgsException(MISSING_STRING, 'x', null);
15 | assertEquals("Could not find string parameter for -x.", e.errorMessage());
16 | }
17 |
18 | public void testInvalidIntegerMessage() throws Exception {
19 | ArgsException e = new ArgsException(INVALID_INTEGER, 'x', "Forty two");
20 | assertEquals("Argument -x expects an integer but was 'Forty two'.", e.errorMessage());
21 | }
22 |
23 | public void testMissingIntegerMessage() throws Exception {
24 | ArgsException e = new ArgsException(MISSING_INTEGER, 'x', null);
25 | assertEquals("Could not find integer parameter for -x.", e.errorMessage());
26 | }
27 |
28 | public void testInvalidDoubleMessage() throws Exception {
29 | ArgsException e = new ArgsException(INVALID_DOUBLE, 'x', "Forty two");
30 | assertEquals("Argument -x expects a double but was 'Forty two'.", e.errorMessage());
31 | }
32 |
33 | public void testMissingDoubleMessage() throws Exception {
34 | ArgsException e = new ArgsException(MISSING_DOUBLE, 'x', null);
35 | assertEquals("Could not find double parameter for -x.", e.errorMessage());
36 | }
37 |
38 | public void testInvalidArgumentName() throws Exception {
39 | ArgsException e = new ArgsException(INVALID_ARGUMENT_NAME, '#', null);
40 | assertEquals("'#' is not a valid argument name.", e.errorMessage());
41 | }
42 |
43 | public void testInvalidFormat() throws Exception {
44 | ArgsException e = new ArgsException(INVALID_ARGUMENT_FORMAT, 'x', "$");
45 | assertEquals("'$' is not a valid argument format.", e.errorMessage());
46 | }
47 | }
48 |
49 |
--------------------------------------------------------------------------------
/test/com/cleancoder/args/ArgsTest.java:
--------------------------------------------------------------------------------
1 | package com.cleancoder.args;
2 |
3 | import org.junit.Test;
4 |
5 | import java.util.Map;
6 |
7 | import static com.cleancoder.args.ArgsException.ErrorCode.*;
8 | import static org.junit.Assert.*;
9 |
10 | public class ArgsTest {
11 |
12 | @Test
13 | public void testCreateWithNoSchemaOrArguments() throws Exception {
14 | Args args = new Args("", new String[0]);
15 | assertEquals(0, args.nextArgument());
16 | }
17 |
18 |
19 | @Test
20 | public void testWithNoSchemaButWithOneArgument() throws Exception {
21 | try {
22 | new Args("", new String[]{"-x"});
23 | fail();
24 | } catch (ArgsException e) {
25 | assertEquals(UNEXPECTED_ARGUMENT, e.getErrorCode());
26 | assertEquals('x', e.getErrorArgumentId());
27 | }
28 | }
29 |
30 | @Test
31 | public void testWithNoSchemaButWithMultipleArguments() throws Exception {
32 | try {
33 | new Args("", new String[]{"-x", "-y"});
34 | fail();
35 | } catch (ArgsException e) {
36 | assertEquals(UNEXPECTED_ARGUMENT, e.getErrorCode());
37 | assertEquals('x', e.getErrorArgumentId());
38 | }
39 |
40 | }
41 |
42 | @Test
43 | public void testNonLetterSchema() throws Exception {
44 | try {
45 | new Args("*", new String[]{});
46 | fail("Args constructor should have thrown exception");
47 | } catch (ArgsException e) {
48 | assertEquals(INVALID_ARGUMENT_NAME, e.getErrorCode());
49 | assertEquals('*', e.getErrorArgumentId());
50 | }
51 | }
52 |
53 | @Test
54 | public void testInvalidArgumentFormat() throws Exception {
55 | try {
56 | new Args("f~", new String[]{});
57 | fail("Args constructor should have throws exception");
58 | } catch (ArgsException e) {
59 | assertEquals(INVALID_ARGUMENT_FORMAT, e.getErrorCode());
60 | assertEquals('f', e.getErrorArgumentId());
61 | }
62 | }
63 |
64 | @Test
65 | public void testSimpleBooleanPresent() throws Exception {
66 | Args args = new Args("x", new String[]{"-x"});
67 | assertEquals(true, args.getBoolean('x'));
68 | assertEquals(1, args.nextArgument());
69 | }
70 |
71 | @Test
72 | public void testSimpleStringPresent() throws Exception {
73 | Args args = new Args("x*", new String[]{"-x", "param"});
74 | assertTrue(args.has('x'));
75 | assertEquals("param", args.getString('x'));
76 | assertEquals(2, args.nextArgument());
77 | }
78 |
79 | @Test
80 | public void testMissingStringArgument() throws Exception {
81 | try {
82 | new Args("x*", new String[]{"-x"});
83 | fail();
84 | } catch (ArgsException e) {
85 | assertEquals(MISSING_STRING, e.getErrorCode());
86 | assertEquals('x', e.getErrorArgumentId());
87 | }
88 | }
89 |
90 | @Test
91 | public void testSpacesInFormat() throws Exception {
92 | Args args = new Args("x, y", new String[]{"-xy"});
93 | assertTrue(args.has('x'));
94 | assertTrue(args.has('y'));
95 | assertEquals(1, args.nextArgument());
96 | }
97 |
98 | @Test
99 | public void testSimpleIntPresent() throws Exception {
100 | Args args = new Args("x#", new String[]{"-x", "42"});
101 | assertTrue(args.has('x'));
102 | assertEquals(42, args.getInt('x'));
103 | assertEquals(2, args.nextArgument());
104 | }
105 |
106 | @Test
107 | public void testInvalidInteger() throws Exception {
108 | try {
109 | new Args("x#", new String[]{"-x", "Forty two"});
110 | fail();
111 | } catch (ArgsException e) {
112 | assertEquals(INVALID_INTEGER, e.getErrorCode());
113 | assertEquals('x', e.getErrorArgumentId());
114 | assertEquals("Forty two", e.getErrorParameter());
115 | }
116 |
117 | }
118 |
119 | @Test
120 | public void testMissingInteger() throws Exception {
121 | try {
122 | new Args("x#", new String[]{"-x"});
123 | fail();
124 | } catch (ArgsException e) {
125 | assertEquals(MISSING_INTEGER, e.getErrorCode());
126 | assertEquals('x', e.getErrorArgumentId());
127 | }
128 | }
129 |
130 | @Test
131 | public void testSimpleDoublePresent() throws Exception {
132 | Args args = new Args("x##", new String[]{"-x", "42.3"});
133 | assertTrue(args.has('x'));
134 | assertEquals(42.3, args.getDouble('x'), .001);
135 | }
136 |
137 | @Test
138 | public void testInvalidDouble() throws Exception {
139 | try {
140 | new Args("x##", new String[]{"-x", "Forty two"});
141 | fail();
142 | } catch (ArgsException e) {
143 | assertEquals(INVALID_DOUBLE, e.getErrorCode());
144 | assertEquals('x', e.getErrorArgumentId());
145 | assertEquals("Forty two", e.getErrorParameter());
146 | }
147 | }
148 |
149 | @Test
150 | public void testMissingDouble() throws Exception {
151 | try {
152 | new Args("x##", new String[]{"-x"});
153 | fail();
154 | } catch (ArgsException e) {
155 | assertEquals(MISSING_DOUBLE, e.getErrorCode());
156 | assertEquals('x', e.getErrorArgumentId());
157 | }
158 | }
159 |
160 | @Test
161 | public void testStringArray() throws Exception {
162 | Args args = new Args("x[*]", new String[]{"-x", "alpha"});
163 | assertTrue(args.has('x'));
164 | String[] result = args.getStringArray('x');
165 | assertEquals(1, result.length);
166 | assertEquals("alpha", result[0]);
167 | }
168 |
169 | @Test
170 | public void testMissingStringArrayElement() throws Exception {
171 | try {
172 | new Args("x[*]", new String[] {"-x"});
173 | fail();
174 | } catch (ArgsException e) {
175 | assertEquals(MISSING_STRING,e.getErrorCode());
176 | assertEquals('x', e.getErrorArgumentId());
177 | }
178 | }
179 |
180 | @Test
181 | public void manyStringArrayElements() throws Exception {
182 | Args args = new Args("x[*]", new String[]{"-x", "alpha", "-x", "beta", "-x", "gamma"});
183 | assertTrue(args.has('x'));
184 | String[] result = args.getStringArray('x');
185 | assertEquals(3, result.length);
186 | assertEquals("alpha", result[0]);
187 | assertEquals("beta", result[1]);
188 | assertEquals("gamma", result[2]);
189 | }
190 |
191 | @Test
192 | public void MapArgument() throws Exception {
193 | Args args = new Args("f&", new String[] {"-f", "key1:val1,key2:val2"});
194 | assertTrue(args.has('f'));
195 | Map map = args.getMap('f');
196 | assertEquals("val1", map.get("key1"));
197 | assertEquals("val2", map.get("key2"));
198 | }
199 |
200 | @Test(expected=ArgsException.class)
201 | public void malFormedMapArgument() throws Exception {
202 | Args args = new Args("f&", new String[] {"-f", "key1:val1,key2"});
203 | }
204 |
205 | @Test
206 | public void oneMapArgument() throws Exception {
207 | Args args = new Args("f&", new String[] {"-f", "key1:val1"});
208 | assertTrue(args.has('f'));
209 | Map map = args.getMap('f');
210 | assertEquals("val1", map.get("key1"));
211 | }
212 |
213 | @Test
214 | public void testExtraArguments() throws Exception {
215 | Args args = new Args("x,y*", new String[]{"-x", "-y", "alpha", "beta"});
216 | assertTrue(args.getBoolean('x'));
217 | assertEquals("alpha", args.getString('y'));
218 | assertEquals(3, args.nextArgument());
219 | }
220 |
221 | @Test
222 | public void testExtraArgumentsThatLookLikeFlags() throws Exception {
223 | Args args = new Args("x,y", new String[]{"-x", "alpha", "-y", "beta"});
224 | assertTrue(args.has('x'));
225 | assertFalse(args.has('y'));
226 | assertTrue(args.getBoolean('x'));
227 | assertFalse(args.getBoolean('y'));
228 | assertEquals(1, args.nextArgument());
229 | }
230 | }
231 |
--------------------------------------------------------------------------------
/test/smc/UtilitiesTest.java:
--------------------------------------------------------------------------------
1 | package smc;
2 |
3 | import de.bechte.junit.runners.context.HierarchicalContextRunner;
4 | import org.junit.Test;
5 | import org.junit.runner.RunWith;
6 |
7 | import static org.hamcrest.CoreMatchers.is;
8 | import static org.junit.Assert.assertThat;
9 | import static smc.Utilities.compressWhiteSpace;
10 |
11 | @RunWith(HierarchicalContextRunner.class)
12 | public class UtilitiesTest {
13 |
14 | public class CompressWhiteSpace {
15 | @Test
16 | public void emptyString() throws Exception {
17 | assertThat(compressWhiteSpace(""), is(""));
18 | }
19 |
20 | @Test
21 | public void NoWhiteSpace() throws Exception {
22 | assertThat(compressWhiteSpace("stringwithnowhitespace"), is("stringwithnowhitespace"));
23 | }
24 |
25 | @Test
26 | public void oneSpace() throws Exception {
27 | assertThat(compressWhiteSpace("one space"), is("one space"));
28 | }
29 |
30 | @Test
31 | public void manyWordsWithSingleSpaces() throws Exception {
32 | assertThat(compressWhiteSpace("many words with single spaces"), is("many words with single spaces"));
33 | }
34 |
35 | @Test
36 | public void oneTab() throws Exception {
37 | assertThat(compressWhiteSpace("one\ttab"), is("one tab"));
38 | }
39 |
40 | @Test
41 | public void twoTabs() throws Exception {
42 | assertThat(compressWhiteSpace("two\t\ttabs"), is("two tabs"));
43 | }
44 |
45 | @Test
46 | public void oneReturn() throws Exception {
47 | assertThat(compressWhiteSpace("one\nreturn"), is("one\nreturn"));
48 | }
49 |
50 | @Test
51 | public void returnsAndSpaces() throws Exception {
52 | assertThat(compressWhiteSpace("word \n word"), is("word\nword"));
53 | }
54 |
55 | @Test
56 | public void startingWhitespace() throws Exception {
57 | assertThat(compressWhiteSpace("\n this"), is("\nthis"));
58 | }
59 |
60 | @Test
61 | public void acceptanceTest() throws Exception {
62 | assertThat(compressWhiteSpace("this is\n\na\t\t string \n \twith\n\nmany\n\n\n\t whitespaces"),
63 | is("this is\na string\nwith\nmany\nwhitespaces"));
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/test/smc/generators/nestedSwitchCaseGenerator/NSCGeneratorTest.java:
--------------------------------------------------------------------------------
1 | package smc.generators.nestedSwitchCaseGenerator;
2 |
3 | import de.bechte.junit.runners.context.HierarchicalContextRunner;
4 | import org.junit.Before;
5 | import org.junit.Test;
6 | import org.junit.runner.RunWith;
7 | import smc.OptimizedStateMachine;
8 | import smc.lexer.Lexer;
9 | import smc.optimizer.Optimizer;
10 | import smc.parser.Parser;
11 | import smc.parser.SyntaxBuilder;
12 | import smc.semanticAnalyzer.SemanticStateMachine;
13 | import smc.semanticAnalyzer.SemanticAnalyzer;
14 |
15 | import static org.hamcrest.Matchers.equalTo;
16 | import static org.junit.Assert.assertThat;
17 | import static smc.generators.nestedSwitchCaseGenerator.NSCNode.*;
18 | import static smc.parser.ParserEvent.EOF;
19 |
20 | @RunWith(HierarchicalContextRunner.class)
21 | public class NSCGeneratorTest {
22 | private Lexer lexer;
23 | private Parser parser;
24 | private SyntaxBuilder builder;
25 | private SemanticAnalyzer analyzer;
26 | private Optimizer optimizer;
27 | private String stdHead = "Initial: I FSM:f Actions:acts";
28 | private NSCGenerator generator;
29 | private NSCNodeVisitor implementer;
30 | private String output = "";
31 |
32 | @Before
33 | public void setUp() throws Exception {
34 | builder = new SyntaxBuilder();
35 | parser = new Parser(builder);
36 | lexer = new Lexer(parser);
37 | analyzer = new SemanticAnalyzer();
38 | optimizer = new Optimizer();
39 | generator = new NSCGenerator();
40 | }
41 |
42 | private OptimizedStateMachine produceStateMachine(String fsmSyntax) {
43 | lexer.lex(fsmSyntax);
44 | parser.handleEvent(EOF, -1, -1);
45 | SemanticStateMachine ast = analyzer.analyze(builder.getFsm());
46 | return optimizer.optimize(ast);
47 | }
48 |
49 | private OptimizedStateMachine headerAndSttToSm(String header, String stt) {
50 | return produceStateMachine(header + " " + stt);
51 | }
52 |
53 | private void assertGenerated(String stt, String switchCase) {
54 | OptimizedStateMachine sm = headerAndSttToSm(stdHead, stt);
55 | generator.generate(sm).accept(implementer);
56 | assertThat(output, equalTo(switchCase));
57 | }
58 |
59 | private class EmptyVisitor implements NSCNodeVisitor {
60 | public void visit(SwitchCaseNode switchCaseNode) {
61 |
62 | }
63 |
64 | public void visit(CaseNode caseNode) {
65 |
66 | }
67 |
68 | public void visit(FunctionCallNode functionCallNode) {
69 |
70 | }
71 |
72 | public void visit(EnumNode enumNode) {
73 |
74 | }
75 |
76 | public void visit(StatePropertyNode statePropertyNode) {
77 |
78 | }
79 |
80 | public void visit(EventDelegatorsNode eventDelegatorsNode) {
81 |
82 | }
83 |
84 | public void visit(HandleEventNode handleEventNode) {
85 | handleEventNode.switchCase.accept(this);
86 | }
87 |
88 |
89 | public void visit(EnumeratorNode enumeratorNode) {
90 | output += enumeratorNode.enumeration + "." + enumeratorNode.enumerator;
91 | }
92 |
93 | public void visit(DefaultCaseNode defaultCaseNode) {
94 | output += String.format(" default(%s);", defaultCaseNode.state);
95 | }
96 |
97 | public void visit(FSMClassNode fsmClassNode) {
98 | fsmClassNode.delegators.accept(this);
99 | fsmClassNode.stateEnum.accept(this);
100 | fsmClassNode.eventEnum.accept(this);
101 | fsmClassNode.stateProperty.accept(this);
102 | fsmClassNode.handleEvent.accept(this);
103 | }
104 | }
105 |
106 | public class SwitchCaseTests {
107 | @Before
108 | public void setup() {
109 | implementer = new TestVisitor();
110 | }
111 |
112 | @Test
113 | public void OneTransition() throws Exception {
114 | assertGenerated(
115 | "{I e I a}",
116 | "s state {case I {s event {case e {setState(State.I) a() } default(I);}}}");
117 | }
118 |
119 | @Test
120 | public void twoTransitions() throws Exception {
121 | assertGenerated("{I e1 S a1 S e2 I a2}",
122 | "" +
123 | "s state {" +
124 | "case I {s event {case e1 {setState(State.S) a1() } default(I);}}" +
125 | "case S {s event {case e2 {setState(State.I) a2() } default(S);}}" +
126 | "}");
127 | }
128 |
129 | @Test
130 | public void twoStatesTwoEventsFourActions() throws Exception {
131 | assertGenerated(
132 | "" +
133 | "{" +
134 | " I e1 S a1 " +
135 | " I e2 - a2" +
136 | " S e1 I a3" +
137 | " S e2 - a4" +
138 | "}",
139 | "" +
140 | "s state {" +
141 | "case I {s event {case e1 {setState(State.S) a1() }" +
142 | "case e2 {setState(State.I) a2() } default(I);}}" +
143 | "case S {s event {case e1 {setState(State.I) a3() }" +
144 | "case e2 {setState(State.S) a4() } default(S);}}}");
145 | }
146 | } // SwitchCase Tests.
147 |
148 | private class TestVisitor extends EmptyVisitor {
149 | public void visit(SwitchCaseNode switchCaseNode) {
150 | output += String.format("s %s {", switchCaseNode.variableName);
151 | switchCaseNode.generateCases(implementer);
152 | output += "}";
153 | }
154 |
155 | public void visit(CaseNode caseNode) {
156 | output += "case " + caseNode.caseName;
157 | output += " {";
158 | caseNode.caseActionNode.accept(this);
159 | output += "}";
160 | }
161 |
162 | public void visit(FunctionCallNode functionCallNode) {
163 | output += String.format("%s(", functionCallNode.functionName);
164 | if (functionCallNode.argument != null)
165 | functionCallNode.argument.accept(this);
166 | output += ") ";
167 | }
168 | }
169 |
170 | public class EnumTests {
171 | @Before
172 | public void setup() {
173 | implementer = new EnumVisitor();
174 | }
175 |
176 | @Test
177 | public void statesAndEvents() throws Exception {
178 | assertGenerated(
179 | "" +
180 | "{" +
181 | " I e1 S a1 " +
182 | " I e2 - a2" +
183 | " S e1 I a3" +
184 | " S e2 - a4" +
185 | "}",
186 | "enum State [I, S] enum Event [e1, e2] ");
187 | }
188 | } //EnumTests
189 |
190 | private class EnumVisitor extends EmptyVisitor {
191 | public void visit(EnumNode enumNode) {
192 | output += String.format("enum %s %s ", enumNode.name, enumNode.enumerators);
193 | }
194 | }
195 |
196 | public class StatePropertyTest {
197 | @Before
198 | public void setup() {
199 | implementer = new StatePropertyVisitor();
200 | }
201 |
202 | @Test
203 | public void statePropertyIsCreated() throws Exception {
204 | assertGenerated("{I e I a}", "state property = I");
205 | }
206 |
207 | } // statePropertyTest
208 |
209 | private class StatePropertyVisitor extends EmptyVisitor {
210 |
211 | public void visit(StatePropertyNode statePropertyNode) {
212 | output += "state property = " + statePropertyNode.initialState;
213 | }
214 | }
215 |
216 | public class EventDelegators {
217 | @Before
218 | public void setup() {
219 | implementer = new EventDelegatorVisitor();
220 | }
221 |
222 | @Test
223 | public void eventDelegatorsAreGenerated() throws Exception {
224 | assertGenerated(
225 | "" +
226 | "{" +
227 | " I e1 S a1 " +
228 | " I e2 - a2" +
229 | " S e1 I a3" +
230 | " S e2 - a4" +
231 | "}",
232 | "delegators [e1, e2]");
233 | }
234 | } // EventDelegators
235 |
236 | private class EventDelegatorVisitor extends EmptyVisitor {
237 |
238 | public void visit(EventDelegatorsNode eventDelegatorsNode) {
239 | output += "delegators " + eventDelegatorsNode.events;
240 | }
241 | }
242 |
243 | public class HandleEventTest {
244 | @Before
245 | public void setup() {
246 | implementer = new HandleEventVisitor();
247 | }
248 |
249 | @Test
250 | public void handleEventIsGenerated() throws Exception {
251 | assertGenerated("{I e I a}", "he(s)");
252 | }
253 |
254 | } // HandleEventTest
255 |
256 | private class HandleEventVisitor extends EmptyVisitor {
257 |
258 | public void visit(SwitchCaseNode switchCaseNode) {
259 | output += "s";
260 | }
261 |
262 | public void visit(HandleEventNode handleEventNode) {
263 | output += "he(";
264 | handleEventNode.switchCase.accept(this);
265 | output += ")";
266 | }
267 | }
268 |
269 | public class FsmClassTest {
270 | @Before
271 | public void setup() {
272 | implementer = new FSMClassVisitor();
273 | }
274 |
275 | @Test
276 | public void fsmClassNodeIsGenerated() throws Exception {
277 | assertGenerated("{I e I a}", "class f:acts {d e e p he sc}");
278 | }
279 |
280 | } // FsmClassTest
281 |
282 | private class FSMClassVisitor extends EmptyVisitor {
283 |
284 | public void visit(SwitchCaseNode switchCaseNode) {
285 | output += "sc";
286 | }
287 |
288 | public void visit(EnumNode enumNode) {
289 | output += "e ";
290 | }
291 |
292 | public void visit(StatePropertyNode statePropertyNode) {
293 | output+="p ";
294 | }
295 |
296 | public void visit(EventDelegatorsNode eventDelegatorsNode) {
297 | output+="d ";
298 | }
299 |
300 | public void visit(HandleEventNode handleEventNode) {
301 | output+="he ";
302 | }
303 |
304 | public void visit(FSMClassNode fsmClassNode) {
305 | output += String.format("class %s:%s {", fsmClassNode.className, fsmClassNode.actionsName);
306 | fsmClassNode.delegators.accept(this);
307 | fsmClassNode.stateEnum.accept(this);
308 | fsmClassNode.eventEnum.accept(this);
309 | fsmClassNode.stateProperty.accept(this);
310 | fsmClassNode.handleEvent.accept(this);
311 | fsmClassNode.handleEvent.switchCase.accept(this);
312 | output += "}";
313 | }
314 | }
315 | }
316 |
--------------------------------------------------------------------------------
/test/smc/implementers/CNestedSwitchCaseImplementerTest.java:
--------------------------------------------------------------------------------
1 | package smc.implementers;
2 |
3 | import org.junit.Before;
4 | import org.junit.Test;
5 | import smc.OptimizedStateMachine;
6 | import smc.generators.nestedSwitchCaseGenerator.NSCGenerator;
7 | import smc.generators.nestedSwitchCaseGenerator.NSCNode;
8 | import smc.lexer.Lexer;
9 | import smc.optimizer.Optimizer;
10 | import smc.parser.Parser;
11 | import smc.parser.SyntaxBuilder;
12 | import smc.semanticAnalyzer.SemanticStateMachine;
13 | import smc.semanticAnalyzer.SemanticAnalyzer;
14 |
15 | import java.util.HashMap;
16 |
17 | import static org.hamcrest.CoreMatchers.is;
18 | import static org.hamcrest.Matchers.equalTo;
19 | import static org.junit.Assert.assertThat;
20 | import static smc.Utilities.compressWhiteSpace;
21 | import static smc.parser.ParserEvent.EOF;
22 |
23 | public class CNestedSwitchCaseImplementerTest {
24 | private Lexer lexer;
25 | private Parser parser;
26 | private SyntaxBuilder builder;
27 | private SemanticAnalyzer analyzer;
28 | private Optimizer optimizer;
29 | private NSCGenerator generator;
30 | private CNestedSwitchCaseImplementer implementer;
31 |
32 | private void assertWhiteSpaceEquivalent(String generated, String expected) {
33 | assertThat(compressWhiteSpace(generated), equalTo(compressWhiteSpace(expected)));
34 | }
35 |
36 | @Before
37 | public void setUp() throws Exception {
38 | builder = new SyntaxBuilder();
39 | parser = new Parser(builder);
40 | lexer = new Lexer(parser);
41 | analyzer = new SemanticAnalyzer();
42 | optimizer = new Optimizer();
43 | generator = new NSCGenerator();
44 | implementer = new CNestedSwitchCaseImplementer(new HashMap<>());
45 | }
46 |
47 | private OptimizedStateMachine produceStateMachine(String fsmSyntax) {
48 | lexer.lex(fsmSyntax);
49 | parser.handleEvent(EOF, -1, -1);
50 | SemanticStateMachine ast = analyzer.analyze(builder.getFsm());
51 | return optimizer.optimize(ast);
52 | }
53 |
54 |
55 | @Test
56 | public void noAction_shouldBeError() throws Exception {
57 | OptimizedStateMachine sm = produceStateMachine("" +
58 | "Initial: I\n" +
59 | "Fsm: fsm\n" +
60 | "{" +
61 | " I E I A" +
62 | "}");
63 | NSCNode generatedFsm = generator.generate(sm);
64 | generatedFsm.accept(implementer);
65 | assertThat(implementer.getErrors().size(), is(1));
66 | assertThat(implementer.getErrors().get(0), is(CNestedSwitchCaseImplementer.Error.NO_ACTION));
67 | }
68 |
69 | @Test
70 | public void oneTransition() throws Exception {
71 | OptimizedStateMachine sm = produceStateMachine("" +
72 | "Initial: I\n" +
73 | "Fsm: fsm\n" +
74 | "Actions: acts\n" +
75 | "{" +
76 | " I E I A" +
77 | "}");
78 | NSCNode generatedFsm = generator.generate(sm);
79 | generatedFsm.accept(implementer);
80 | assertWhiteSpaceEquivalent(implementer.getFsmHeader(), "" +
81 | "#ifndef FSM_H\n" +
82 | "#define FSM_H\n" +
83 | "struct acts;\n" +
84 | "struct fsm;\n" +
85 | "struct fsm *make_fsm(struct acts*);\n" +
86 | "void fsm_E(struct fsm*);\n" +
87 | "#endif\n");
88 |
89 | assertWhiteSpaceEquivalent(implementer.getFsmImplementation(), "" +
90 | "#include \n" +
91 | "#include \"acts.h\"\n" +
92 | "#include \"fsm.h\"\n" +
93 | "" +
94 | "enum Event {E};\n" +
95 | "enum State {I};\n" +
96 | "" +
97 | "struct fsm {\n" +
98 | " enum State state;\n" +
99 | " struct acts *actions;\n" +
100 | "};\n" +
101 | "" +
102 | "struct fsm *make_fsm(struct acts* actions) {\n" +
103 | " struct fsm *fsm = malloc(sizeof(struct fsm));\n" +
104 | " fsm->actions = actions;\n" +
105 | " fsm->state = I;\n" +
106 | " return fsm;\n" +
107 | "}\n" +
108 | "" +
109 | "static void setState(struct fsm *fsm, enum State state) {\n" +
110 | " fsm->state = state;\n" +
111 | "}\n" +
112 | "" +
113 | "static void A(struct fsm *fsm) {\n" +
114 | " fsm->actions->A();\n" +
115 | "}\n" +
116 | "" +
117 | "static void processEvent(enum State state, enum Event event, struct fsm *fsm, char *event_name) {\n" +
118 | " switch (state) {\n" +
119 | " case I:\n" +
120 | " switch (event) {\n" +
121 | " case E:\n" +
122 | " setState(fsm, I);\n" +
123 | " A(fsm);\n" +
124 | " break;\n" +
125 | " default:\n" +
126 | " (fsm->actions->unexpected_transition)(\"I\", event_name);\n" +
127 | " break;\n" +
128 | " }\n" +
129 | " break;\n" +
130 | " }\n" +
131 | "}\n" +
132 | "" +
133 | "void fsm_E(struct fsm* fsm) {\n" +
134 | " processEvent(fsm->state, E, fsm, \"E\");\n" +
135 | "}\n");
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/test/smc/implementers/CppNestedSwitchCaseImplementerTests.java:
--------------------------------------------------------------------------------
1 | package smc.implementers;
2 |
3 | import de.bechte.junit.runners.context.HierarchicalContextRunner;
4 | import org.junit.Before;
5 | import org.junit.Test;
6 | import org.junit.runner.RunWith;
7 | import smc.OptimizedStateMachine;
8 | import smc.generators.nestedSwitchCaseGenerator.NSCGenerator;
9 | import smc.generators.nestedSwitchCaseGenerator.NSCNode;
10 | import smc.lexer.Lexer;
11 | import smc.optimizer.Optimizer;
12 | import smc.parser.Parser;
13 | import smc.parser.SyntaxBuilder;
14 | import smc.semanticAnalyzer.SemanticStateMachine;
15 | import smc.semanticAnalyzer.SemanticAnalyzer;
16 |
17 | import java.util.HashMap;
18 |
19 | import static org.hamcrest.CoreMatchers.is;
20 | import static org.hamcrest.Matchers.equalTo;
21 | import static org.junit.Assert.assertThat;
22 | import static smc.Utilities.compressWhiteSpace;
23 | import static smc.parser.ParserEvent.EOF;
24 |
25 | @RunWith(HierarchicalContextRunner.class)
26 | public class CppNestedSwitchCaseImplementerTests {
27 | private Lexer lexer;
28 | private Parser parser;
29 | private SyntaxBuilder builder;
30 | private SemanticAnalyzer analyzer;
31 | private Optimizer optimizer;
32 | private NSCGenerator generator;
33 | private CppNestedSwitchCaseImplementer implementer;
34 |
35 | @Before
36 | public void setUp() throws Exception {
37 | builder = new SyntaxBuilder();
38 | parser = new Parser(builder);
39 | lexer = new Lexer(parser);
40 | analyzer = new SemanticAnalyzer();
41 | optimizer = new Optimizer();
42 | generator = new NSCGenerator();
43 | }
44 |
45 | private OptimizedStateMachine produceStateMachine(String fsmSyntax) {
46 | lexer.lex(fsmSyntax);
47 | parser.handleEvent(EOF, -1, -1);
48 | SemanticStateMachine ast = analyzer.analyze(builder.getFsm());
49 | return optimizer.optimize(ast);
50 | }
51 |
52 | private void assertWhitespaceEquivalent(String generatedCode, String expected) {
53 | assertThat(compressWhiteSpace(generatedCode), equalTo(compressWhiteSpace(expected)));
54 | }
55 |
56 | public class TestsWithNoFlags {
57 |
58 | @Before
59 | public void setup() {
60 | implementer = new CppNestedSwitchCaseImplementer(new HashMap<>());
61 | }
62 |
63 | @Test
64 | public void noActions_shouldBeError() throws Exception {
65 | OptimizedStateMachine sm = produceStateMachine("" +
66 | "Initial: I\n" +
67 | "Fsm: fsm\n" +
68 | "{" +
69 | " I E I A" +
70 | "}");
71 | NSCNode generatedFsm = generator.generate(sm);
72 | generatedFsm.accept(implementer);
73 | assertThat(implementer.getErrors().size(), is(1));
74 | assertThat(implementer.getErrors().get(0), is(CppNestedSwitchCaseImplementer.Error.NO_ACTIONS));
75 | }
76 | @Test
77 | public void oneTransition() throws Exception {
78 | OptimizedStateMachine sm = produceStateMachine("" +
79 | "Initial: I\n" +
80 | "Fsm: fsm\n" +
81 | "Actions: acts\n" +
82 | "{" +
83 | " I E I A" +
84 | "}");
85 | NSCNode generatedFsm = generator.generate(sm);
86 | generatedFsm.accept(implementer);
87 |
88 | assertWhitespaceEquivalent(implementer.getOutput(), "" +
89 | "#ifndef FSM_H\n" +
90 | "#define FSM_H\n" +
91 | "#include \"acts.h\"\n" +
92 | "" +
93 | "class fsm : public acts {\n" +
94 | "public:\n" +
95 | " fsm()\n" +
96 | " : state(State_I)\n" +
97 | " {}\n" +
98 | "" +
99 | " void E() {processEvent(Event_E, \"E\");}\n" +
100 | "" +
101 | "private:\n" +
102 | " enum State {State_I};\n" +
103 | " State state;\n" +
104 | "" +
105 | " void setState(State s) {state=s;}\n" +
106 | "" +
107 | " enum Event {Event_E};\n" +
108 | "" +
109 | " void processEvent(Event event, const char* eventName) {\n" +
110 | " switch (state) {\n" +
111 | " case State_I:\n" +
112 | " switch (event) {\n" +
113 | " case Event_E:\n" +
114 | " setState(State_I);\n" +
115 | " A();\n" +
116 | " break;\n" +
117 | "" +
118 | " default:\n" +
119 | " unexpected_transition(\"I\", eventName);\n" +
120 | " break;\n" +
121 | " }\n" +
122 | " break;\n" +
123 | " }\n" +
124 | " }\n" +
125 | "};\n" +
126 | "#endif\n");
127 | }
128 | } // no flags
129 | }
130 |
--------------------------------------------------------------------------------
/test/smc/implementers/JavaNestedSwitchCaseImplementerTest.java:
--------------------------------------------------------------------------------
1 | package smc.implementers;
2 |
3 | import org.junit.Before;
4 | import org.junit.Test;
5 | import smc.OptimizedStateMachine;
6 | import smc.generators.nestedSwitchCaseGenerator.NSCGenerator;
7 | import smc.generators.nestedSwitchCaseGenerator.NSCNode;
8 | import smc.lexer.Lexer;
9 | import smc.optimizer.Optimizer;
10 | import smc.parser.Parser;
11 | import smc.parser.SyntaxBuilder;
12 | import smc.semanticAnalyzer.SemanticStateMachine;
13 | import smc.semanticAnalyzer.SemanticAnalyzer;
14 |
15 | import java.util.HashMap;
16 | import java.util.Map;
17 |
18 | import static org.hamcrest.Matchers.*;
19 | import static org.junit.Assert.assertThat;
20 | import static smc.Utilities.compressWhiteSpace;
21 | import static smc.parser.ParserEvent.EOF;
22 |
23 | public class JavaNestedSwitchCaseImplementerTest {
24 | private Lexer lexer;
25 | private Parser parser;
26 | private SyntaxBuilder builder;
27 | private SemanticAnalyzer analyzer;
28 | private Optimizer optimizer;
29 | private NSCGenerator generator;
30 | private Map emptyFlags = new HashMap<>();
31 |
32 | @Before
33 | public void setUp() throws Exception {
34 | builder = new SyntaxBuilder();
35 | parser = new Parser(builder);
36 | lexer = new Lexer(parser);
37 | analyzer = new SemanticAnalyzer();
38 | optimizer = new Optimizer();
39 | generator = new NSCGenerator();
40 | }
41 |
42 | private OptimizedStateMachine produceStateMachine(String fsmSyntax) {
43 | lexer.lex(fsmSyntax);
44 | parser.handleEvent(EOF, -1, -1);
45 | SemanticStateMachine ast = analyzer.analyze(builder.getFsm());
46 | return optimizer.optimize(ast);
47 | }
48 |
49 | @Test
50 | public void oneTransitionWithPackageAndActions() throws Exception {
51 | Map flags = new HashMap<>();
52 | flags.put("package", "thePackage");
53 | JavaNestedSwitchCaseImplementer implementer = new JavaNestedSwitchCaseImplementer(flags);
54 | OptimizedStateMachine sm = produceStateMachine("" +
55 | "Initial: I\n" +
56 | "Fsm: fsm\n" +
57 | "Actions: acts\n" +
58 | "{" +
59 | " I E I A" +
60 | "}");
61 | NSCNode generatedFsm = generator.generate(sm);
62 | generatedFsm.accept(implementer);
63 | assertWhitespaceEquivalent(implementer.getOutput(), "" +
64 | "package thePackage;\n" +
65 | "public abstract class fsm implements acts {\n" +
66 | "public abstract void unhandledTransition(String state, String event);\n" +
67 | " private enum State {I}\n" +
68 | " private enum Event {E}\n" +
69 | " private State state = State.I;\n" +
70 | "" +
71 | " private void setState(State s) {state = s;}\n" +
72 | " public void E() {handleEvent(Event.E);}\n" +
73 | " private void handleEvent(Event event) {\n" +
74 | " switch(state) {\n" +
75 | " case I:\n" +
76 | " switch(event) {\n" +
77 | " case E:\n" +
78 | " setState(State.I);\n" +
79 | " A();\n" +
80 | " break;\n" +
81 | " default: unhandledTransition(state.name(), event.name()); break;\n" +
82 | " }\n" +
83 | " break;\n" +
84 | " }\n" +
85 | " }\n" +
86 | "}\n");
87 | }
88 |
89 | private void assertWhitespaceEquivalent(String generated, String expected) {
90 | assertThat(compressWhiteSpace(generated), equalTo(compressWhiteSpace(expected)));
91 | }
92 |
93 | @Test
94 | public void oneTransitionWithActionsButNoPackage() throws Exception {
95 | JavaNestedSwitchCaseImplementer implementer = new JavaNestedSwitchCaseImplementer(emptyFlags);
96 | OptimizedStateMachine sm = produceStateMachine("" +
97 | "Initial: I\n" +
98 | "Fsm: fsm\n" +
99 | "Actions: acts\n" +
100 | "{" +
101 | " I E I A" +
102 | "}");
103 | NSCNode generatedFsm = generator.generate(sm);
104 | generatedFsm.accept(implementer);
105 | assertThat(implementer.getOutput(), startsWith("" +
106 | "public abstract class fsm implements acts {\n"));
107 | }
108 |
109 | @Test
110 | public void oneTransitionWithNoActionsAndNoPackage() throws Exception {
111 | JavaNestedSwitchCaseImplementer implementer = new JavaNestedSwitchCaseImplementer(emptyFlags);
112 | OptimizedStateMachine sm = produceStateMachine("" +
113 | "Initial: I\n" +
114 | "Fsm: fsm\n" +
115 | "{" +
116 | " I E I A" +
117 | "}");
118 | NSCNode generatedFsm = generator.generate(sm);
119 | generatedFsm.accept(implementer);
120 | String output = implementer.getOutput();
121 | assertThat(output, startsWith("" +
122 | "public abstract class fsm {\n"));
123 | assertThat(output, containsString("protected abstract void A();\n"));
124 | }
125 |
126 | }
127 |
--------------------------------------------------------------------------------
/test/smc/lexer/LexerTest.java:
--------------------------------------------------------------------------------
1 | package smc.lexer;
2 |
3 | import de.bechte.junit.runners.context.HierarchicalContextRunner;
4 | import org.junit.Before;
5 | import org.junit.Test;
6 | import org.junit.runner.RunWith;
7 |
8 | import static org.junit.Assert.assertEquals;
9 |
10 | @RunWith(HierarchicalContextRunner.class)
11 | public class LexerTest implements TokenCollector {
12 | String tokens = "";
13 | Lexer lexer;
14 | private boolean firstToken = true;
15 |
16 | @Before
17 | public void setUp() throws Exception {
18 | lexer = new Lexer(this);
19 | }
20 |
21 | private void addToken(String token) {
22 | if (!firstToken)
23 | tokens += ",";
24 | tokens += token;
25 | firstToken = false;
26 | }
27 |
28 | private void assertLexResult(String input, String expected) {
29 | lexer.lex(input);
30 | assertEquals(expected, tokens);
31 | }
32 |
33 | public void openBrace(int line, int pos) {
34 | addToken("OB");
35 | }
36 |
37 | public void closedBrace(int line, int pos) {
38 | addToken("CB");
39 | }
40 |
41 | public void openParen(int line, int pos) {
42 | addToken("OP");
43 | }
44 |
45 | public void closedParen(int line, int pos) {
46 | addToken("CP");
47 | }
48 |
49 | public void openAngle(int line, int pos) {
50 | addToken("OA");
51 | }
52 |
53 | public void closedAngle(int line, int pos) {
54 | addToken("CA");
55 | }
56 |
57 | public void dash(int line, int pos) {
58 | addToken("D");
59 | }
60 |
61 | public void colon(int line, int pos) {
62 | addToken("C");
63 | }
64 |
65 | public void name(String name, int line, int pos) {
66 | addToken("#" + name + "#");
67 | }
68 |
69 | public void error(int line, int pos) {
70 | addToken("E" + line + "/" + pos);
71 | }
72 |
73 | public class SingleTokenTests {
74 | @Test
75 | public void findsOpenBrace() throws Exception {
76 | assertLexResult("{", "OB");
77 | }
78 |
79 | @Test
80 | public void findsClosedBrace() throws Exception {
81 | assertLexResult("}", "CB");
82 | }
83 |
84 | @Test
85 | public void findsOpenParen() throws Exception {
86 | assertLexResult("(", "OP");
87 | }
88 |
89 | @Test
90 | public void findsClosedParen() throws Exception {
91 | assertLexResult(")", "CP");
92 | }
93 |
94 | @Test
95 | public void findsOpenAngle() throws Exception {
96 | assertLexResult("<", "OA");
97 | }
98 |
99 | @Test
100 | public void findsClosedAngle() throws Exception {
101 | assertLexResult(">", "CA");
102 | }
103 |
104 | @Test
105 | public void findsDash() throws Exception {
106 | assertLexResult("-", "D");
107 | }
108 |
109 | @Test
110 | public void findStarAsDash() throws Exception {
111 | assertLexResult("*", "D");
112 | }
113 |
114 | @Test
115 | public void findsColon() throws Exception {
116 | assertLexResult(":", "C");
117 | }
118 |
119 | @Test
120 | public void findsSimpleName() throws Exception {
121 | assertLexResult("name", "#name#");
122 | }
123 |
124 | @Test
125 | public void findComplexName() throws Exception {
126 | assertLexResult("Room_222", "#Room_222#");
127 | }
128 |
129 | @Test
130 | public void error() throws Exception {
131 | assertLexResult(".", "E1/1");
132 | }
133 |
134 | @Test
135 | public void nothingButWhiteSpace() throws Exception {
136 | assertLexResult(" ", "");
137 | }
138 |
139 | @Test
140 | public void whiteSpaceBefore() throws Exception {
141 | assertLexResult(" \t\n -", "D");
142 | }
143 | }
144 |
145 | public class CommentTests {
146 | @Test
147 | public void commentAfterToken() throws Exception {
148 | assertLexResult("-//comment\n", "D");
149 | }
150 |
151 | @Test
152 | public void commentLines() throws Exception {
153 | assertLexResult("//comment 1\n-//comment2\n//comment3\n-//comment4;", "D,D");
154 | }
155 | }
156 |
157 | public class MultipleTokenTests {
158 | @Test
159 | public void simpleSequence() throws Exception {
160 | assertLexResult("{}", "OB,CB");
161 | }
162 |
163 | @Test
164 | public void complexSequence() throws Exception {
165 | assertLexResult("FSM:fsm{this}", "#FSM#,C,#fsm#,OB,#this#,CB");
166 | }
167 |
168 | @Test
169 | public void allTokens() throws Exception {
170 | assertLexResult("{}()<>-: name .", "OB,CB,OP,CP,OA,CA,D,C,#name#,E1/15");
171 | }
172 |
173 | @Test
174 | public void multipleLines() throws Exception {
175 | assertLexResult("FSM:fsm.\n{bob-.}", "#FSM#,C,#fsm#,E1/8,OB,#bob#,D,E2/6,CB");
176 | }
177 | }
178 |
179 | }
180 |
--------------------------------------------------------------------------------
/test/smc/optimizer/OptimizerTest.java:
--------------------------------------------------------------------------------
1 | package smc.optimizer;
2 |
3 | import de.bechte.junit.runners.context.HierarchicalContextRunner;
4 | import org.junit.Before;
5 | import org.junit.Test;
6 | import org.junit.runner.RunWith;
7 | import smc.OptimizedStateMachine;
8 | import smc.lexer.Lexer;
9 | import smc.parser.Parser;
10 | import smc.parser.SyntaxBuilder;
11 | import smc.semanticAnalyzer.SemanticAnalyzer;
12 | import smc.semanticAnalyzer.SemanticStateMachine;
13 |
14 | import static org.hamcrest.Matchers.*;
15 | import static org.hamcrest.core.IsEqual.equalTo;
16 | import static org.junit.Assert.assertThat;
17 | import static smc.Utilities.compressWhiteSpace;
18 | import static smc.parser.ParserEvent.EOF;
19 |
20 | @RunWith(HierarchicalContextRunner.class)
21 | public class OptimizerTest {
22 | private Lexer lexer;
23 | private Parser parser;
24 | private SyntaxBuilder builder;
25 | private SemanticAnalyzer analyzer;
26 | private Optimizer optimizer;
27 | private OptimizedStateMachine optimizedStateMachine;
28 |
29 | @Before
30 | public void setUp() throws Exception {
31 | builder = new SyntaxBuilder();
32 | parser = new Parser(builder);
33 | lexer = new Lexer(parser);
34 | analyzer = new SemanticAnalyzer();
35 | optimizer = new Optimizer();
36 | }
37 |
38 | private OptimizedStateMachine produceStateMachineWithHeader(String s) {
39 | String fsmSyntax = "fsm:f initial:i actions:a " + s;
40 | return produceStateMachine(fsmSyntax);
41 | }
42 |
43 | private OptimizedStateMachine produceStateMachine(String fsmSyntax) {
44 | lexer.lex(fsmSyntax);
45 | parser.handleEvent(EOF, -1, -1);
46 | SemanticStateMachine ast = analyzer.analyze(builder.getFsm());
47 | return optimizer.optimize(ast);
48 | }
49 |
50 | private void assertOptimization(String syntax, String stateMachine) {
51 | optimizedStateMachine = produceStateMachineWithHeader(syntax);
52 | assertThat(
53 | compressWhiteSpace(optimizedStateMachine.transitionsToString()),
54 | equalTo(compressWhiteSpace(stateMachine)));
55 | }
56 |
57 | public class BasicOptimizerFunctions {
58 | @Test
59 | public void header() throws Exception {
60 | OptimizedStateMachine sm = produceStateMachineWithHeader("{i e i -}");
61 | assertThat(sm.header.fsm, equalTo("f"));
62 | assertThat(sm.header.initial, equalTo("i"));
63 | assertThat(sm.header.actions, equalTo("a"));
64 | }
65 |
66 | @Test
67 | public void statesArePreserved() throws Exception {
68 | OptimizedStateMachine sm = produceStateMachineWithHeader("{i e s - s e i -}");
69 | assertThat(sm.states, contains("i", "s"));
70 | }
71 |
72 | @Test
73 | public void abstractStatesAreRemoved() throws Exception {
74 | OptimizedStateMachine sm = produceStateMachineWithHeader("{(b) - - - i:b e i -}");
75 | assertThat(sm.states, not(hasItems("b")));
76 | }
77 |
78 | @Test
79 | public void eventsArePreserved() throws Exception {
80 | OptimizedStateMachine sm = produceStateMachineWithHeader("{i e1 s - s e2 i -}");
81 | assertThat(sm.events, contains("e1", "e2"));
82 | }
83 |
84 | @Test
85 | public void actionsArePreserved() throws Exception {
86 | OptimizedStateMachine sm = produceStateMachineWithHeader("{i e1 s a1 s e2 i a2}");
87 | assertThat(sm.actions, contains("a1", "a2"));
88 | }
89 |
90 | @Test
91 | public void simpleStateMachine() throws Exception {
92 | assertOptimization(
93 | "" +
94 | "{i e i a1}",
95 |
96 | "" +
97 | "i {\n" +
98 | " e i {a1}\n" +
99 | "}\n");
100 | assertThat(optimizedStateMachine.transitions, hasSize(1));
101 |
102 | }
103 | } // Basic Optimizer Functions
104 |
105 | public class EntryAndExitActions {
106 | @Test
107 | public void entryFunctionsAdded() throws Exception {
108 | assertOptimization(
109 | "" +
110 | "{" +
111 | " i e s a1" +
112 | " i e2 s a2" +
113 | " s x2 >x1 e s a1" +
131 | " i e2 s a2" +
132 | " s e i -" +
133 | "}",
134 | "" +
135 | "i {\n" +
136 | " e s {x2 x1 a1}\n" +
137 | " e2 s {x2 x1 a2}\n" +
138 | "}\n" +
139 | "s {\n" +
140 | " e i {}\n" +
141 | "}\n");
142 | }
143 |
144 | @Test
145 | public void firstSuperStateEntryAndExitActionsAreAdded() throws Exception {
146 | assertOptimization(
147 | "" +
148 | "{" +
149 | " (ib) >ibx1 >ibx2 - - -" +
150 | " (sb) x e s a" +
152 | " s:sb ib1x - - -" +
169 | " (ib2) : ib1 >ib2x - - -" +
170 | " (sb1) x e s a" +
173 | " s:sb2 ib1x - - -" +
190 | " (ib2) : ib1 >ib2x - - -" +
191 | " (ib3) : ib1 >ib3x - - -" +
192 | " (sb1) x e s a" +
196 | " s :sb2 :sb3 alarmOff - - -" +
362 | "" +
363 | " FirstCoin : Base {" +
364 | " Pass Alarming -" +
365 | " Coin Unlocked unlock" +
366 | " }" +
367 | "" +
368 | " Unlocked : Base {" +
369 | " Pass Locked lock" +
370 | " Coin - thankyou" +
371 | "}");
372 | assertThat(sm.toString(), equalTo(
373 | "" +
374 | "Initial: Locked\n" +
375 | "Fsm: TwoCoinTurnstile\n" +
376 | "Actions:Turnstile\n" +
377 | "{\n" +
378 | " Alarming {\n" +
379 | " Reset Locked {alarmOff lock}\n" +
380 | " }\n" +
381 | " FirstCoin {\n" +
382 | " Pass Alarming {alarmOn}\n" +
383 | " Coin Unlocked {unlock}\n" +
384 | " Reset Locked {lock}\n" +
385 | " }\n" +
386 | " Locked {\n" +
387 | " Pass Alarming {alarmOn}\n" +
388 | " Coin FirstCoin {}\n" +
389 | " Reset Locked {lock}\n" +
390 | " }\n" +
391 | " Unlocked {\n" +
392 | " Pass Locked {lock}\n" +
393 | " Coin Unlocked {thankyou}\n" +
394 | " Reset Locked {lock}\n" +
395 | " }\n" +
396 | "}\n"));
397 | }
398 | } // Acceptance Tests
399 | }
400 |
--------------------------------------------------------------------------------
/test/smc/parser/ParserTest.java:
--------------------------------------------------------------------------------
1 | package smc.parser;
2 |
3 | import de.bechte.junit.runners.context.HierarchicalContextRunner;
4 | import org.junit.Before;
5 | import org.junit.Test;
6 | import org.junit.runner.RunWith;
7 | import smc.lexer.Lexer;
8 |
9 | import static org.junit.Assert.assertEquals;
10 | import static smc.parser.ParserEvent.EOF;
11 |
12 | @RunWith(HierarchicalContextRunner.class)
13 | public class ParserTest {
14 | private Lexer lexer;
15 | private Parser parser;
16 | private SyntaxBuilder builder;
17 |
18 | @Before
19 | public void setUp() throws Exception {
20 | builder = new SyntaxBuilder();
21 | parser = new Parser(builder);
22 | lexer = new Lexer(parser);
23 | }
24 |
25 | private void assertParseResult(String s, String expected) {
26 | lexer.lex(s);
27 | parser.handleEvent(EOF, -1, -1);
28 | assertEquals(expected, builder.getFsm().toString());
29 | }
30 |
31 | private void assertParseError(String s, String expected) {
32 | lexer.lex(s);
33 | parser.handleEvent(EOF, -1, -1);
34 | assertEquals(expected, builder.getFsm().getError());
35 | }
36 |
37 | public class IncrementalTests {
38 | @Test
39 | public void parseOneHeader() throws Exception {
40 | assertParseResult("N:V{}", "N:V\n.\n");
41 | }
42 |
43 | @Test
44 | public void parseManyHeaders() throws Exception {
45 | assertParseResult(" N1 : V1\tN2 : V2\n{}", "N1:V1\nN2:V2\n.\n");
46 | }
47 |
48 | @Test
49 | public void noHeader() throws Exception {
50 | assertParseResult(" {}", ".\n");
51 | }
52 |
53 | @Test
54 | public void simpleTransition() throws Exception {
55 | assertParseResult("{ s e ns a }",
56 | "" +
57 | "{\n" +
58 | " s e ns a\n" +
59 | "}\n" +
60 | ".\n");
61 | }
62 |
63 | @Test
64 | public void transitionWithNullAction() throws Exception {
65 | assertParseResult("{s e ns -}",
66 | "" +
67 | "{\n" +
68 | " s e ns {}\n" +
69 | "}\n" +
70 | ".\n");
71 | }
72 |
73 | @Test
74 | public void transitionWithManyActions() throws Exception {
75 | assertParseResult("{s e ns {a1 a2}}",
76 | "" +
77 | "{\n" +
78 | " s e ns {a1 a2}\n" +
79 | "}\n" +
80 | ".\n");
81 | }
82 |
83 | @Test
84 | public void stateWithSubTransition() throws Exception {
85 | assertParseResult("{s {e ns a}}",
86 | "" +
87 | "{\n" +
88 | " s e ns a\n" +
89 | "}\n" +
90 | ".\n");
91 | }
92 |
93 | @Test
94 | public void stateWithSeveralSubTransitions() throws Exception {
95 | assertParseResult("{s {e1 ns a1 e2 ns a2}}",
96 | "" +
97 | "{\n" +
98 | " s {\n" +
99 | " e1 ns a1\n" +
100 | " e2 ns a2\n" +
101 | " }\n" +
102 | "}\n" +
103 | ".\n");
104 | }
105 |
106 | @Test
107 | public void manyTransitions() throws Exception {
108 | assertParseResult("{s1 e1 s2 a1 s2 e2 s3 a2}",
109 | "" +
110 | "{\n" +
111 | " s1 e1 s2 a1\n" +
112 | " s2 e2 s3 a2\n" +
113 | "}\n" +
114 | ".\n");
115 | }
116 |
117 | @Test
118 | public void superState() throws Exception {
119 | assertParseResult("{(ss) e s a}",
120 | "" +
121 | "{\n" +
122 | " (ss) e s a\n" +
123 | "}\n" +
124 | ".\n");
125 | }
126 |
127 | @Test
128 | public void entryAction() throws Exception {
129 | assertParseResult("{s xa e ns a}",
140 | "" +
141 | "{\n" +
142 | " s >xa e ns a\n" +
143 | "}\n" +
144 | ".\n");
145 | }
146 |
147 | @Test
148 | public void derivedState() throws Exception {
149 | assertParseResult("{s:ss e ns a}",
150 | "" +
151 | "{\n" +
152 | " s:ss e ns a\n" +
153 | "}\n" +
154 | ".\n");
155 | }
156 |
157 | @Test
158 | public void allStateAdornments() throws Exception {
159 | assertParseResult("{(s)xa:ss e ns a}",
160 | "" +
161 | "{\n" +
162 | " (s):ss xa e ns a\n" +
163 | "}\n" +
164 | ".\n");
165 | }
166 |
167 | @Test
168 | public void stateWithNoSubTransitions() throws Exception {
169 | assertParseResult("{s {}}",
170 | "" +
171 | "{\n" +
172 | " s {\n" +
173 | " }\n" +
174 | "}\n" +
175 | ".\n");
176 | }
177 |
178 | @Test
179 | public void stateWithAllDashes() throws Exception {
180 | assertParseResult("{s - - -}",
181 | "" +
182 | "{\n" +
183 | " s null null {}\n" +
184 | "}\n" +
185 | ".\n");
186 | }
187 |
188 | @Test
189 | public void multipleSuperStates() throws Exception {
190 | assertParseResult("{s :x :y - - -}",
191 | "" +
192 | "{\n" +
193 | " s:x:y null null {}\n" +
194 | "}\n" +
195 | ".\n");
196 | }
197 |
198 | @Test
199 | public void multipleEntryActions() throws Exception {
200 | assertParseResult("{s x >y - - -}",
211 | "" +
212 | "{\n" +
213 | " s >x >y null null {}\n" +
214 | "}\n" +
215 | ".\n");
216 | }
217 |
218 | @Test
219 | public void multipleEntryAndExitActionsWithBraces() throws Exception {
220 | assertParseResult("{s <{u v} >{w x} - - -}",
221 | "" +
222 | "{\n" +
223 | " s w >x null null {}\n" +
224 | "}\n" +
225 | ".\n");
226 | }
227 | }
228 |
229 | public class AcceptanceTests {
230 | @Test
231 | public void simpleOneCoinTurnstile() throws Exception {
232 | assertParseResult(
233 | "" +
234 | "Actions: Turnstile\n" +
235 | "FSM: OneCoinTurnstile\n" +
236 | "Initial: Locked\n" +
237 | "{\n" +
238 | " Locked\tCoin\tUnlocked\t{alarmOff unlock}\n" +
239 | " Locked \tPass\tLocked\t\talarmOn\n" +
240 | " Unlocked\tCoin\tUnlocked\tthankyou\n" +
241 | " Unlocked\tPass\tLocked\t\tlock\n" +
242 | "}",
243 | "" +
244 | "Actions:Turnstile\n" +
245 | "FSM:OneCoinTurnstile\n" +
246 | "Initial:Locked\n" +
247 | "{\n" +
248 | " Locked Coin Unlocked {alarmOff unlock}\n" +
249 | " Locked Pass Locked alarmOn\n" +
250 | " Unlocked Coin Unlocked thankyou\n" +
251 | " Unlocked Pass Locked lock\n" +
252 | "}\n" +
253 | ".\n"
254 | );
255 | }
256 |
257 | @Test
258 | public void twoCoinTurnstileWithoutSuperState() throws Exception {
259 | assertParseResult(
260 | "" +
261 | "Actions: Turnstile\n" +
262 | "FSM: TwoCoinTurnstile\n" +
263 | "Initial: Locked\n" +
264 | "{\n" +
265 | "\tLocked {\n" +
266 | "\t\tPass\tAlarming\talarmOn\n" +
267 | "\t\tCoin\tFirstCoin\t-\n" +
268 | "\t\tReset\tLocked\t{lock alarmOff}\n" +
269 | "\t}\n" +
270 | "\t\n" +
271 | "\tAlarming\tReset\tLocked {lock alarmOff}\n" +
272 | "\t\n" +
273 | "\tFirstCoin {\n" +
274 | "\t\tPass\tAlarming\t-\n" +
275 | "\t\tCoin\tUnlocked\tunlock\n" +
276 | "\t\tReset\tLocked {lock alarmOff}\n" +
277 | "\t}\n" +
278 | "\t\n" +
279 | "\tUnlocked {\n" +
280 | "\t\tPass\tLocked\tlock\n" +
281 | "\t\tCoin\t-\t\tthankyou\n" +
282 | "\t\tReset\tLocked {lock alarmOff}\n" +
283 | "\t}\n" +
284 | "}",
285 | "" +
286 | "Actions:Turnstile\n" +
287 | "FSM:TwoCoinTurnstile\n" +
288 | "Initial:Locked\n" +
289 | "{\n" +
290 | " Locked {\n" +
291 | " Pass Alarming alarmOn\n" +
292 | " Coin FirstCoin {}\n" +
293 | " Reset Locked {lock alarmOff}\n" +
294 | " }\n" +
295 | " Alarming Reset Locked {lock alarmOff}\n" +
296 | " FirstCoin {\n" +
297 | " Pass Alarming {}\n" +
298 | " Coin Unlocked unlock\n" +
299 | " Reset Locked {lock alarmOff}\n" +
300 | " }\n" +
301 | " Unlocked {\n" +
302 | " Pass Locked lock\n" +
303 | " Coin null thankyou\n" +
304 | " Reset Locked {lock alarmOff}\n" +
305 | " }\n" +
306 | "}\n" +
307 | ".\n"
308 | );
309 | }
310 |
311 | @Test
312 | public void twoCoinTurnstileWithSuperState() throws Exception {
313 | assertParseResult(
314 | "" +
315 | "Actions: Turnstile\n" +
316 | "FSM: TwoCoinTurnstile\n" +
317 | "Initial: Locked\n" +
318 | "{\n" +
319 | " (Base)\tReset\tLocked\tlock\n" +
320 | "\n" +
321 | "\tLocked : Base {\n" +
322 | "\t\tPass\tAlarming\t-\n" +
323 | "\t\tCoin\tFirstCoin\t-\n" +
324 | "\t}\n" +
325 | "\t\n" +
326 | "\tAlarming : Base\talarmOff -\t-\t-\n" +
327 | "\t\n" +
328 | "\tFirstCoin : Base {\n" +
329 | "\t\tPass\tAlarming\t-\n" +
330 | "\t\tCoin\tUnlocked\tunlock\n" +
331 | "\t}\n" +
332 | "\t\n" +
333 | "\tUnlocked : Base {\n" +
334 | "\t\tPass\tLocked\tlock\n" +
335 | "\t\tCoin\t-\t\tthankyou\n" +
336 | "\t}\n" +
337 | "}",
338 | "" +
339 | "Actions:Turnstile\n" +
340 | "FSM:TwoCoinTurnstile\n" +
341 | "Initial:Locked\n" +
342 | "{\n" +
343 | " (Base) Reset Locked lock\n" +
344 | " Locked:Base {\n" +
345 | " Pass Alarming {}\n" +
346 | " Coin FirstCoin {}\n" +
347 | " }\n" +
348 | " Alarming:Base alarmOff null null {}\n" +
349 | " FirstCoin:Base {\n" +
350 | " Pass Alarming {}\n" +
351 | " Coin Unlocked unlock\n" +
352 | " }\n" +
353 | " Unlocked:Base {\n" +
354 | " Pass Locked lock\n" +
355 | " Coin null thankyou\n" +
356 | " }\n" +
357 | "}\n" +
358 | ".\n");
359 | }
360 | }
361 |
362 | public class ErrorTests {
363 | @Test
364 | public void parseNothing() throws Exception {
365 | assertParseError("", "Syntax error: HEADER. HEADER|EOF. line -1, position -1.\n");
366 | }
367 |
368 | @Test
369 | public void headerWithNoColonOrValue() throws Exception {
370 | assertParseError("A {s e ns a}",
371 | "Syntax error: HEADER. HEADER_COLON|OPEN_BRACE. line 1, position 2.\n");
372 | }
373 |
374 | @Test
375 | public void headerWithNoValue() throws Exception {
376 | assertParseError("A: {s e ns a}",
377 | "Syntax error: HEADER. HEADER_VALUE|OPEN_BRACE. line 1, position 3.\n");
378 | }
379 |
380 | @Test
381 | public void transitionWayTooShort() throws Exception {
382 | assertParseError("{s}",
383 | "Syntax error: STATE. STATE_MODIFIER|CLOSED_BRACE. line 1, position 2.\n");
384 | }
385 |
386 | @Test
387 | public void transitionTooShort() throws Exception {
388 | assertParseError("{s e}",
389 | "Syntax error: TRANSITION. SINGLE_EVENT|CLOSED_BRACE. line 1, position 4.\n");
390 | }
391 |
392 | @Test
393 | public void transitionNoAction() throws Exception {
394 | assertParseError("{s e ns}",
395 | "Syntax error: TRANSITION. SINGLE_NEXT_STATE|CLOSED_BRACE. line 1, position 7.\n");
396 | }
397 |
398 | @Test
399 | public void noClosingBrace() throws Exception {
400 | assertParseError("{",
401 | "Syntax error: STATE. STATE_SPEC|EOF. line -1, position -1.\n");
402 | }
403 |
404 | @Test
405 | public void initialStateDash() throws Exception {
406 | assertParseError("{- e ns a}",
407 | "Syntax error: STATE. STATE_SPEC|DASH. line 1, position 1.\n");
408 | }
409 |
410 | @Test
411 | public void lexicalError() throws Exception {
412 | assertParseError("{.}",
413 | "Syntax error: SYNTAX. . line 1, position 2.\n");
414 | }
415 | }
416 | }
417 |
--------------------------------------------------------------------------------
/test_cases/Ice/RootFSMGen.java:
--------------------------------------------------------------------------------
1 | public abstract class RootFSMGen {
2 | public abstract void unhandledTransition(String state, String event);
3 | private enum State {autoBatch,batchSplashAuto,batchSplashManual,determiningUserMode,end,gettingAutoBatch,gettingManualBatch,init,login,manualBatch,pageAutoBatch,pageAutoBatchStopped,pageManualBatch,processingAutoBatch,processingAutoBatchStopped,processingManualBatch}
4 | private enum Event {cancel,init,auto,nextBatchFound,select,refresh,login,manual,requeue,openPage,exit,goBack,stop,reject,batchesFound,itemChanged,redisplay,noBatchFound,ok,complete,setZone,assign}
5 | private State state = State.init;
6 | private void setState(State s) {state = s;}
7 | public void cancel() {handleEvent(Event.cancel);}
8 | public void init() {handleEvent(Event.init);}
9 | public void auto() {handleEvent(Event.auto);}
10 | public void nextBatchFound() {handleEvent(Event.nextBatchFound);}
11 | public void select() {handleEvent(Event.select);}
12 | public void refresh() {handleEvent(Event.refresh);}
13 | public void login() {handleEvent(Event.login);}
14 | public void manual() {handleEvent(Event.manual);}
15 | public void requeue() {handleEvent(Event.requeue);}
16 | public void openPage() {handleEvent(Event.openPage);}
17 | public void exit() {handleEvent(Event.exit);}
18 | public void goBack() {handleEvent(Event.goBack);}
19 | public void stop() {handleEvent(Event.stop);}
20 | public void reject() {handleEvent(Event.reject);}
21 | public void batchesFound() {handleEvent(Event.batchesFound);}
22 | public void itemChanged() {handleEvent(Event.itemChanged);}
23 | public void redisplay() {handleEvent(Event.redisplay);}
24 | public void noBatchFound() {handleEvent(Event.noBatchFound);}
25 | public void ok() {handleEvent(Event.ok);}
26 | public void complete() {handleEvent(Event.complete);}
27 | public void setZone() {handleEvent(Event.setZone);}
28 | public void assign() {handleEvent(Event.assign);}
29 | private void handleEvent(Event event) {
30 | switch(state) {
31 | case autoBatch:
32 | switch(event) {
33 | case manual:
34 | setState(State.gettingManualBatch);
35 | isBatchAvailable();
36 | createSelector();
37 | break;
38 | case select:
39 | setState(State.gettingAutoBatch);
40 | getNextAutoBatch();
41 | createSelector();
42 | break;
43 | case itemChanged:
44 | setState(State.autoBatch);
45 | setUserAuto();
46 | displayThumbnailAuto();
47 | workTypeItemChanged();
48 | break;
49 | case redisplay:
50 | setState(State.autoBatch);
51 | setUserAuto();
52 | displayThumbnailAuto();
53 | displayThumbnailAuto();
54 | break;
55 | case exit:
56 | setState(State.end);
57 | exitProgram();
58 | break;
59 | default: unhandledTransition(state.name(), event.name()); break;
60 | }
61 | break;
62 | case batchSplashAuto:
63 | switch(event) {
64 | case ok:
65 | setState(State.processingAutoBatch);
66 | hideBatchSplashScreen();
67 | allMode();
68 | initBatch();
69 | displayAutoThumbnailProcessing();
70 | break;
71 | case complete:
72 | setState(State.gettingAutoBatch);
73 | hideBatchSplashScreen();
74 | getNextAutoBatch();
75 | completeBatch();
76 | hideThumbnailScreen();
77 | break;
78 | default: unhandledTransition(state.name(), event.name()); break;
79 | }
80 | break;
81 | case batchSplashManual:
82 | switch(event) {
83 | case ok:
84 | setState(State.processingManualBatch);
85 | hideBatchSplashScreen();
86 | allMode();
87 | initBatch();
88 | displayManualThumbnailProcessing();
89 | break;
90 | case complete:
91 | setState(State.determiningUserMode);
92 | hideBatchSplashScreen();
93 | cleanupThumbnails();
94 | checkUserState();
95 | completeBatch();
96 | hideThumbnailScreen();
97 | break;
98 | default: unhandledTransition(state.name(), event.name()); break;
99 | }
100 | break;
101 | case determiningUserMode:
102 | switch(event) {
103 | case auto:
104 | setState(State.autoBatch);
105 | setUserAuto();
106 | displayThumbnailAuto();
107 | break;
108 | case manual:
109 | setState(State.gettingManualBatch);
110 | isBatchAvailable();
111 | createSelector();
112 | break;
113 | default: unhandledTransition(state.name(), event.name()); break;
114 | }
115 | break;
116 | case end:
117 | switch(event) {
118 | default: unhandledTransition(state.name(), event.name()); break;
119 | }
120 | break;
121 | case gettingAutoBatch:
122 | switch(event) {
123 | case nextBatchFound:
124 | setState(State.batchSplashAuto);
125 | displayBatchSplashScreen();
126 | break;
127 | case noBatchFound:
128 | setState(State.determiningUserMode);
129 | cleanupThumbnails();
130 | checkUserState();
131 | noBatchDialog();
132 | break;
133 | default: unhandledTransition(state.name(), event.name()); break;
134 | }
135 | break;
136 | case gettingManualBatch:
137 | switch(event) {
138 | case batchesFound:
139 | setState(State.manualBatch);
140 | setUserManual();
141 | displayThumbnailManual();
142 | break;
143 | case noBatchFound:
144 | setState(State.autoBatch);
145 | setUserAuto();
146 | displayThumbnailAuto();
147 | break;
148 | default: unhandledTransition(state.name(), event.name()); break;
149 | }
150 | break;
151 | case init:
152 | switch(event) {
153 | case init:
154 | setState(State.login);
155 | displayLoginScreen();
156 | break;
157 | default: unhandledTransition(state.name(), event.name()); break;
158 | }
159 | break;
160 | case login:
161 | switch(event) {
162 | case login:
163 | setState(State.determiningUserMode);
164 | hideLoginScreen();
165 | cleanupThumbnails();
166 | checkUserState();
167 | break;
168 | case cancel:
169 | setState(State.end);
170 | hideLoginScreen();
171 | exitProgram();
172 | break;
173 | default: unhandledTransition(state.name(), event.name()); break;
174 | }
175 | break;
176 | case manualBatch:
177 | switch(event) {
178 | case auto:
179 | setState(State.autoBatch);
180 | setUserAuto();
181 | displayThumbnailAuto();
182 | break;
183 | case refresh:
184 | setState(State.gettingManualBatch);
185 | isBatchAvailable();
186 | break;
187 | case select:
188 | setState(State.batchSplashManual);
189 | displayBatchSplashScreen();
190 | selectManualBatch();
191 | break;
192 | case redisplay:
193 | setState(State.manualBatch);
194 | setUserManual();
195 | displayThumbnailManual();
196 | displayThumbnailManual();
197 | break;
198 | case exit:
199 | setState(State.end);
200 | exitProgram();
201 | break;
202 | default: unhandledTransition(state.name(), event.name()); break;
203 | }
204 | break;
205 | case pageAutoBatch:
206 | switch(event) {
207 | case goBack:
208 | setState(State.processingAutoBatch);
209 | hidePageScreen();
210 | displayAutoThumbnailProcessing();
211 | break;
212 | case assign:
213 | setState(State.page);
214 | hidePageScreen();
215 | displayPageScreen();
216 | assignPage();
217 | redisplayPageScreen();
218 | break;
219 | case setZone:
220 | setState(State.page);
221 | hidePageScreen();
222 | displayPageScreen();
223 | assignZone();
224 | redisplayPageScreen();
225 | break;
226 | default: unhandledTransition(state.name(), event.name()); break;
227 | }
228 | break;
229 | case pageAutoBatchStopped:
230 | switch(event) {
231 | case goBack:
232 | setState(State.processingAutoBatchStopped);
233 | hidePageScreen();
234 | displayAutoThumbnailProcessing();
235 | break;
236 | case assign:
237 | setState(State.page);
238 | hidePageScreen();
239 | displayPageScreen();
240 | assignPage();
241 | redisplayPageScreen();
242 | break;
243 | case setZone:
244 | setState(State.page);
245 | hidePageScreen();
246 | displayPageScreen();
247 | assignZone();
248 | redisplayPageScreen();
249 | break;
250 | default: unhandledTransition(state.name(), event.name()); break;
251 | }
252 | break;
253 | case pageManualBatch:
254 | switch(event) {
255 | case goBack:
256 | setState(State.processingManualBatch);
257 | hidePageScreen();
258 | displayManualThumbnailProcessing();
259 | break;
260 | case assign:
261 | setState(State.page);
262 | hidePageScreen();
263 | displayPageScreen();
264 | assignPage();
265 | redisplayPageScreen();
266 | break;
267 | case setZone:
268 | setState(State.page);
269 | hidePageScreen();
270 | displayPageScreen();
271 | assignZone();
272 | redisplayPageScreen();
273 | break;
274 | default: unhandledTransition(state.name(), event.name()); break;
275 | }
276 | break;
277 | case processingAutoBatch:
278 | switch(event) {
279 | case stop:
280 | setState(State.processingAutoBatchStopped);
281 | hideThumbnailScreen();
282 | break;
283 | case complete:
284 | setState(State.gettingAutoBatch);
285 | hideThumbnailScreen();
286 | getNextAutoBatch();
287 | completeBatch();
288 | cleanupBatch();
289 | break;
290 | case reject:
291 | setState(State.gettingAutoBatch);
292 | hideThumbnailScreen();
293 | getNextAutoBatch();
294 | rejectBatch();
295 | cleanupBatch();
296 | break;
297 | case openPage:
298 | setState(State.pageAutoBatch);
299 | hideThumbnailScreen();
300 | displayPageScreen();
301 | break;
302 | case redisplay:
303 | setState(State.processingAutoBatch);
304 | hideThumbnailScreen();
305 | displayAutoThumbnailProcessing();
306 | break;
307 | case ok:
308 | setState(State.processingBatch);
309 | hideThumbnailScreen();
310 | break;
311 | case cancel:
312 | setState(State.processingBatch);
313 | hideThumbnailScreen();
314 | break;
315 | case requeue:
316 | setState(State.determiningUserMode);
317 | hideThumbnailScreen();
318 | cleanupThumbnails();
319 | checkUserState();
320 | requeueBatch();
321 | cleanupBatch();
322 | break;
323 | case assign:
324 | setState(State.processingBatch);
325 | hideThumbnailScreen();
326 | assignPage();
327 | break;
328 | case exit:
329 | setState(State.end);
330 | hideThumbnailScreen();
331 | exitProgram();
332 | requeueBatch();
333 | break;
334 | default: unhandledTransition(state.name(), event.name()); break;
335 | }
336 | break;
337 | case processingAutoBatchStopped:
338 | switch(event) {
339 | case complete:
340 | setState(State.determiningUserMode);
341 | hideThumbnailScreen();
342 | cleanupThumbnails();
343 | checkUserState();
344 | completeBatch();
345 | cleanupBatch();
346 | break;
347 | case reject:
348 | setState(State.determiningUserMode);
349 | hideThumbnailScreen();
350 | cleanupThumbnails();
351 | checkUserState();
352 | rejectBatch();
353 | cleanupBatch();
354 | break;
355 | case openPage:
356 | setState(State.pageAutoBatchStopped);
357 | hideThumbnailScreen();
358 | displayPageScreen();
359 | break;
360 | case stop:
361 | setState(State.processingAutoBatch);
362 | hideThumbnailScreen();
363 | break;
364 | case redisplay:
365 | setState(State.processingAutoBatchStopped);
366 | hideThumbnailScreen();
367 | displayAutoThumbnailProcessing();
368 | break;
369 | case ok:
370 | setState(State.processingBatch);
371 | hideThumbnailScreen();
372 | break;
373 | case cancel:
374 | setState(State.processingBatch);
375 | hideThumbnailScreen();
376 | break;
377 | case requeue:
378 | setState(State.determiningUserMode);
379 | hideThumbnailScreen();
380 | cleanupThumbnails();
381 | checkUserState();
382 | requeueBatch();
383 | cleanupBatch();
384 | break;
385 | case assign:
386 | setState(State.processingBatch);
387 | hideThumbnailScreen();
388 | assignPage();
389 | break;
390 | case exit:
391 | setState(State.end);
392 | hideThumbnailScreen();
393 | exitProgram();
394 | requeueBatch();
395 | break;
396 | default: unhandledTransition(state.name(), event.name()); break;
397 | }
398 | break;
399 | case processingManualBatch:
400 | switch(event) {
401 | case openPage:
402 | setState(State.pageManualBatch);
403 | hideThumbnailScreen();
404 | displayPageScreen();
405 | break;
406 | case redisplay:
407 | setState(State.processingManualBatch);
408 | hideThumbnailScreen();
409 | displayManualThumbnailProcessing();
410 | break;
411 | case ok:
412 | setState(State.processingBatch);
413 | hideThumbnailScreen();
414 | break;
415 | case cancel:
416 | setState(State.processingBatch);
417 | hideThumbnailScreen();
418 | break;
419 | case complete:
420 | setState(State.determiningUserMode);
421 | hideThumbnailScreen();
422 | cleanupThumbnails();
423 | checkUserState();
424 | completeBatch();
425 | cleanupBatch();
426 | break;
427 | case requeue:
428 | setState(State.determiningUserMode);
429 | hideThumbnailScreen();
430 | cleanupThumbnails();
431 | checkUserState();
432 | requeueBatch();
433 | cleanupBatch();
434 | break;
435 | case reject:
436 | setState(State.determiningUserMode);
437 | hideThumbnailScreen();
438 | cleanupThumbnails();
439 | checkUserState();
440 | rejectBatch();
441 | cleanupBatch();
442 | break;
443 | case assign:
444 | setState(State.processingBatch);
445 | hideThumbnailScreen();
446 | assignPage();
447 | break;
448 | case exit:
449 | setState(State.end);
450 | hideThumbnailScreen();
451 | exitProgram();
452 | requeueBatch();
453 | break;
454 | default: unhandledTransition(state.name(), event.name()); break;
455 | }
456 | break;
457 | }
458 | }
459 | protected abstract void assignPage();
460 | protected abstract void isBatchAvailable();
461 | protected abstract void cleanupBatch();
462 | protected abstract void initBatch();
463 | protected abstract void hideBatchSplashScreen();
464 | protected abstract void displayThumbnailAuto();
465 | protected abstract void exitProgram();
466 | protected abstract void rejectBatch();
467 | protected abstract void redisplayPageScreen();
468 | protected abstract void displayThumbnailManual();
469 | protected abstract void assignZone();
470 | protected abstract void workTypeItemChanged();
471 | protected abstract void noBatchDialog();
472 | protected abstract void requeueBatch();
473 | protected abstract void checkUserState();
474 | protected abstract void displayAutoThumbnailProcessing();
475 | protected abstract void completeBatch();
476 | protected abstract void cleanupThumbnails();
477 | protected abstract void selectManualBatch();
478 | protected abstract void displayLoginScreen();
479 | protected abstract void displayBatchSplashScreen();
480 | protected abstract void hideLoginScreen();
481 | protected abstract void displayPageScreen();
482 | protected abstract void hidePageScreen();
483 | protected abstract void setUserManual();
484 | protected abstract void hideThumbnailScreen();
485 | protected abstract void getNextAutoBatch();
486 | protected abstract void createSelector();
487 | protected abstract void displayManualThumbnailProcessing();
488 | protected abstract void allMode();
489 | protected abstract void setUserAuto();
490 | }
491 |
--------------------------------------------------------------------------------
/test_cases/Ice/ice.sm:
--------------------------------------------------------------------------------
1 | // ice.sm, the finite state machine for the ICE gui
2 |
3 | Initial: init
4 | FSM: RootFSMGen
5 | {
6 | // life begins in the init state; but it doesn't stay there long.
7 | // Once the main program has done some initialization, it invokes
8 | // the init event which moves us to the login state.
9 | init
10 | {
11 | init login {}
12 | }
13 |
14 | // Whenever we enter the login state we display the login screen.
15 | // Whenever we leave it we hide the login screen. While in the
16 | // login state, only two events can move us out. The login event
17 | // tells us that someone has successfully logged in. The cancel
18 | // event means that the user gave up, and we terminate the program.
19 | login hideLoginScreen
20 | {
21 | login determiningUserMode {}
22 | cancel end {}
23 | }
24 |
25 | // This is a transient state. Upon entry some cleanup is done,
26 | // and then the checkUserState function is called. It determines
27 | // if the user state was set to auto or manual and invokes the
28 | // corresponding event.
29 | determiningUserMode < { cleanupThumbnails checkUserState }
30 | {
31 | auto autoBatch {}
32 | manual gettingManualBatch { createSelector }
33 | }
34 |
35 | // Someone has determined that the system will run in auto batch
36 | // mode. We set up the windows for auto mode and display the
37 | // thumbnail screen. Then we wait for an event.
38 | autoBatch < { setUserAuto displayThumbnailAuto }
39 | {
40 | manual gettingManualBatch { createSelector }
41 | select gettingAutoBatch { createSelector }
42 | itemChanged * workTypeItemChanged
43 | redisplay * displayThumbnailAuto
44 | exit end {}
45 | }
46 |
47 | // We are trying to get a new auto batch from the system. This
48 | // can either succeed or fail.
49 | gettingAutoBatch hideThumbnailScreen
83 | {
84 | // these transitions are to correct button bounce on
85 | // entry into this state
86 | ok * {}
87 | cancel * {}
88 |
89 | // these are the normal transitions
90 | complete determiningUserMode { completeBatch cleanupBatch }
91 | requeue determiningUserMode { requeueBatch cleanupBatch }
92 | reject determiningUserMode { rejectBatch cleanupBatch }
93 | assign * assignPage
94 | exit end requeueBatch
95 | }
96 |
97 | // We are processing an auto-mode batch.
98 | processingAutoBatch : processingBatch
99 | {
100 | stop processingAutoBatchStopped {}
101 | complete gettingAutoBatch { completeBatch cleanupBatch }
102 | reject gettingAutoBatch { rejectBatch cleanupBatch }
103 | openPage pageAutoBatch {}
104 | redisplay * displayAutoThumbnailProcessing
105 | }
106 |
107 | // we are processing an auto mode batch, and the user had pressed
108 | // the stop button, so that auto-mode will come to and end.
109 | processingAutoBatchStopped : processingBatch
110 | {
111 | complete determiningUserMode { completeBatch cleanupBatch }
112 | reject determiningUserMode { rejectBatch cleanupBatch }
113 | openPage pageAutoBatchStopped {}
114 | stop processingAutoBatch {}
115 | redisplay * displayAutoThumbnailProcessing
116 | }
117 |
118 | // we are processing a manual mode batch.
119 | processingManualBatch : processingBatch
120 | {
121 | openPage pageManualBatch {}
122 | redisplay * displayManualThumbnailProcessing
123 | }
124 |
125 | // This superstate is occupied whenever the batch splash screen is
126 | // active. Upon entry it activates the batch splash screen, and
127 | // upon exit, it deactivates it.
128 | (batchSplash) hideBatchSplashScreen
129 | {
130 | }
131 |
132 | // The batch splash screen is up in auto mode.
133 | batchSplashAuto : batchSplash
134 | {
135 | ok processingAutoBatch {allMode initBatch displayAutoThumbnailProcessing}
136 | complete gettingAutoBatch {completeBatch hideThumbnailScreen}
137 | }
138 |
139 | // The batch splash screen is up in manual mode.
140 | batchSplashManual : batchSplash
141 | {
142 | ok processingManualBatch {allMode initBatch
143 | displayManualThumbnailProcessing}
144 | complete determiningUserMode {completeBatch hideThumbnailScreen}
145 |
146 | }
147 |
148 | // This superstate is occupied whenever the page screen is
149 | // active. Entry into this superstate displays the page screen,
150 | // and exit from it hides the page screen.
151 | (page) hidePageScreen
152 | {
153 | assign * {assignPage redisplayPageScreen}
154 | setZone * {assignZone redisplayPageScreen}
155 | }
156 |
157 | // we are displaying the page screen in auto-batch mode.
158 | pageAutoBatch : page
159 | {
160 | goBack processingAutoBatch displayAutoThumbnailProcessing
161 | }
162 |
163 | // we are displaying the page screen in auto mode after the user
164 | // has decided to stop auto-mode.
165 | pageAutoBatchStopped : page
166 | {
167 | goBack processingAutoBatchStopped displayAutoThumbnailProcessing
168 | }
169 |
170 | // we are displaying the page screen in manual batch mode.
171 | pageManualBatch : page
172 | {
173 | goBack processingManualBatch displayManualThumbnailProcessing
174 | }
175 |
176 | // we are done with the program. We immediately exit.
177 | end
2 | #include
3 | #include
4 | #include
5 | #include "twoCoinTurnstile.h"
6 | #include "turnstileActions.h"
7 |
8 | static struct TwoCoinTurnstile* fsm;
9 | static char output[80];
10 | static int position;
11 | static int error_count = 0;
12 |
13 | void lock() {
14 | output[position++] = 'L';
15 | }
16 |
17 | void unlock() {
18 | output[position++] = 'U';
19 | }
20 |
21 | void thankyou() {
22 | output[position++] = 'T';
23 | }
24 |
25 | void alarmOn() {
26 | output[position++] = 'A';
27 | }
28 |
29 | void alarmOff() {
30 | output[position++] = 'O';
31 | }
32 |
33 | void unexpected(char* state, char* event) {
34 | char error[80];
35 | bzero(error, 80);
36 | sprintf(error, "X(%s,%s)", state, event);
37 | strcpy(output+position, error);
38 | position += strlen(error);
39 | }
40 |
41 | static void setup() {
42 | bzero(output, 80);
43 | position = 0;
44 |
45 | struct TurnstileActions *actions = malloc(sizeof(struct TurnstileActions));
46 | actions->lock = lock;
47 | actions->unlock = unlock;
48 | actions->alarmOn = alarmOn;
49 | actions->alarmOff = alarmOff;
50 | actions->thankyou = thankyou;
51 | actions->unexpected_transition = unexpected;
52 |
53 | fsm = make_TwoCoinTurnstile(actions);
54 | }
55 |
56 | void check(char* function, char* expected) {
57 | int result = strcmp(output, expected);
58 | if (result) {
59 | printf("\n%s failed. expected: %s, but was: %s", function, expected, output);
60 | error_count++;
61 | }
62 | else
63 | printf(".");
64 | }
65 |
66 | static void testNormalBehavior() {
67 | setup();
68 | TwoCoinTurnstile_Coin(fsm);
69 | TwoCoinTurnstile_Coin(fsm);
70 | TwoCoinTurnstile_Pass(fsm);
71 | check("testNormalBehavior", "UL");
72 | }
73 |
74 | static void testAlarm() {
75 | setup();
76 | TwoCoinTurnstile_Pass(fsm);
77 | check("testAlarm", "A");
78 | }
79 |
80 | static void testThankyou() {
81 | setup();
82 | TwoCoinTurnstile_Coin(fsm);
83 | TwoCoinTurnstile_Coin(fsm);
84 | TwoCoinTurnstile_Coin(fsm);
85 | check("testThankyou", "UT");
86 | }
87 |
88 | static void testNormalManyThanksAndAlarm() {
89 | setup();
90 | TwoCoinTurnstile_Coin(fsm);
91 | TwoCoinTurnstile_Coin(fsm);
92 | TwoCoinTurnstile_Pass(fsm);
93 | TwoCoinTurnstile_Coin(fsm);
94 | TwoCoinTurnstile_Coin(fsm);
95 | TwoCoinTurnstile_Coin(fsm);
96 | TwoCoinTurnstile_Pass(fsm);
97 | TwoCoinTurnstile_Pass(fsm);
98 | check("testNormalManyThanksAndAlarm", "ULUTLA");
99 | }
100 |
101 |
102 | static void testUndefined() {
103 | setup();
104 | TwoCoinTurnstile_Pass(fsm);
105 | TwoCoinTurnstile_Pass(fsm);
106 | check("testUndefined", "AX(Alarming,Pass)");
107 | }
108 |
109 | static void testReset() {
110 | setup();
111 | TwoCoinTurnstile_Pass(fsm);
112 | TwoCoinTurnstile_Reset(fsm);
113 | check("testReset", "AOL");
114 | }
115 |
116 | int main(int ac, char** av) {
117 | printf("Turnstile test begins\n");
118 | testNormalBehavior();
119 | testAlarm();
120 | testThankyou();
121 | testNormalManyThanksAndAlarm();
122 | testUndefined();
123 | testReset();
124 | if (error_count)
125 | printf("\nTests Complete with %d errors.\n", error_count);
126 | else
127 | printf("\nOK\n");
128 | }
129 |
130 |
--------------------------------------------------------------------------------
/test_cases/c_turnstile/twoCoinTurnstile.sm:
--------------------------------------------------------------------------------
1 | Actions: TurnstileActions
2 | FSM: TwoCoinTurnstile
3 | Initial: Locked
4 | {
5 | (Base) Reset Locked lock
6 |
7 | Locked : Base {
8 | Pass Alarming -
9 | Coin FirstCoin -
10 | }
11 |
12 | Alarming : Base alarmOff {
13 | - - -
14 | }
15 |
16 | FirstCoin : Base {
17 | Pass Alarming -
18 | Coin Unlocked unlock
19 | }
20 |
21 | Unlocked : Base {
22 | Pass Locked lock
23 | Coin - thankyou
24 | }
25 | }
--------------------------------------------------------------------------------
/test_cases/cpp_turnstile/Makefile:
--------------------------------------------------------------------------------
1 | all : clean TwoCoinTurnstile.h turnstile_test
2 | ./turnstile_test
3 |
4 | turnstile_test : turnstile_test.cc
5 |
6 | clean :
7 | rm -f *.o TwoCoinTurnstile.h turnstile_test
8 |
9 | TwoCoinTurnstile.h : twoCoinTurnstile.sm
10 | java -jar ../../build/jar/smc.jar -l Cpp twoCoinTurnstile.sm
11 |
12 |
13 |
--------------------------------------------------------------------------------
/test_cases/cpp_turnstile/TurnstileActions.h:
--------------------------------------------------------------------------------
1 | #ifndef TURNSTILEACTIONS_H
2 | #define TURNSTILEACTIONS_H
3 |
4 | class TurnstileActions {
5 | public:
6 | virtual void lock() = 0;
7 | virtual void unlock() = 0;
8 | virtual void alarmOn() = 0;
9 | virtual void alarmOff() = 0;
10 | virtual void thankyou() = 0;
11 | virtual void unexpected_transition(const char* state, const char* event) = 0;
12 | };
13 |
14 | #endif
--------------------------------------------------------------------------------
/test_cases/cpp_turnstile/turnstile_test.cc:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | #include "TurnstileActions.h"
7 | #include "TwoCoinTurnstile.h"
8 |
9 | static char output[80];
10 | static int position;
11 | static int error_count = 0;
12 |
13 | class MyTwoCoinTurnstile : public TwoCoinTurnstile {
14 | public:
15 | virtual void lock() {
16 | output[position++] = 'L';
17 | }
18 |
19 | void unlock() {
20 | output[position++] = 'U';
21 | }
22 |
23 | void thankyou() {
24 | output[position++] = 'T';
25 | }
26 |
27 | void alarmOn() {
28 | output[position++] = 'A';
29 | }
30 |
31 | void alarmOff() {
32 | output[position++] = 'O';
33 | }
34 |
35 | void unexpected_transition (const char* state, const char* event) {
36 | char error[80];
37 | bzero(error, 80);
38 | sprintf(error, "X(%s,%s)", state, event);
39 | strcpy(output+position, error);
40 | position += strlen(error);
41 | }
42 | };
43 |
44 | static MyTwoCoinTurnstile* fsm;
45 |
46 | static void setup() {
47 | bzero(output, 80);
48 | position = 0;
49 |
50 | fsm = new MyTwoCoinTurnstile();
51 | }
52 |
53 | void check(const char* function, const char* expected) {
54 | int result = strcmp(output, expected);
55 | if (result) {
56 | printf("\n%s failed. expected: %s, but was: %s", function, expected, output);
57 | error_count++;
58 | }
59 | else
60 | printf(".");
61 | }
62 |
63 | static void testNormalBehavior() {
64 | setup();
65 | fsm->Coin();
66 | fsm->Coin();
67 | fsm->Pass();
68 | check("testNormalBehavior", "UL");
69 | }
70 |
71 | static void testAlarm() {
72 | setup();
73 | fsm->Pass();
74 | check("testAlarm", "A");
75 | }
76 |
77 | static void testThankyou() {
78 | setup();
79 | fsm->Coin();
80 | fsm->Coin();
81 | fsm->Coin();
82 | check("testThankyou", "UT");
83 | }
84 |
85 | static void testNormalManyThanksAndAlarm() {
86 | setup();
87 | fsm->Coin();
88 | fsm->Coin();
89 | fsm->Pass();
90 | fsm->Coin();
91 | fsm->Coin();
92 | fsm->Coin();
93 | fsm->Pass();
94 | fsm->Pass();
95 | check("testNormalManyThanksAndAlarm", "ULUTLA");
96 | }
97 |
98 |
99 | static void testUndefined() {
100 | setup();
101 | fsm->Pass();
102 | fsm->Pass();
103 | check("testUndefined", "AX(Alarming,Pass)");
104 | }
105 |
106 | static void testReset() {
107 | setup();
108 | fsm->Pass();
109 | fsm->Reset();
110 | check("testReset", "AOL");
111 | }
112 |
113 | int main(int ac, char** av) {
114 | printf("Turnstile test begins\n");
115 | testNormalBehavior();
116 | testAlarm();
117 | testThankyou();
118 | testNormalManyThanksAndAlarm();
119 | testUndefined();
120 | testReset();
121 | if (error_count)
122 | printf("\nTests Complete with %d errors.\n", error_count);
123 | else
124 | printf("\nOK\n");
125 | }
126 |
127 |
--------------------------------------------------------------------------------
/test_cases/cpp_turnstile/twoCoinTurnstile.sm:
--------------------------------------------------------------------------------
1 | Actions: TurnstileActions
2 | FSM: TwoCoinTurnstile
3 | Initial: Locked
4 | {
5 | (Base) Reset Locked lock
6 |
7 | Locked : Base {
8 | Pass Alarming -
9 | Coin FirstCoin -
10 | }
11 |
12 | Alarming : Base alarmOff {
13 | - - -
14 | }
15 |
16 | FirstCoin : Base {
17 | Pass Alarming -
18 | Coin Unlocked unlock
19 | }
20 |
21 | Unlocked : Base {
22 | Pass Locked lock
23 | Coin - thankyou
24 | }
25 | }
--------------------------------------------------------------------------------
/test_cases/dart_turnstile/Makefile:
--------------------------------------------------------------------------------
1 | all : clean TwoCoinTurnstile.dart turnstile_test.dart
2 | pub get
3 | pub run test turnstile_test.dart
4 |
5 | clean :
6 | rm -f TwoCoinTurnstile.dart
7 |
8 | TwoCoinTurnstile.dart : twoCoinTurnstile.sm
9 | java -jar ../../build/jar/smc.jar -l Dart $?
10 | dartfmt -w *.dart
11 |
--------------------------------------------------------------------------------
/test_cases/dart_turnstile/TurnstileActions.dart:
--------------------------------------------------------------------------------
1 | abstract class TurnstileActions {
2 | lock();
3 | unlock();
4 | alarmOn();
5 | alarmOff();
6 | thankyou();
7 | unexpected_transition(final String state, final String event);
8 | }
9 |
--------------------------------------------------------------------------------
/test_cases/dart_turnstile/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: turnstile
2 |
3 | dependencies:
4 | meta: ^1.2.4
5 |
6 | dev_dependencies:
7 | test: ^1.15.7
8 |
--------------------------------------------------------------------------------
/test_cases/dart_turnstile/turnstile_test.dart:
--------------------------------------------------------------------------------
1 | // -*- compile-command: "pub run test turnstile_test.dart"; -*-
2 |
3 | import 'package:test/test.dart';
4 |
5 | import 'TurnstileActions.dart';
6 | import 'TwoCoinTurnstile.dart';
7 |
8 | class MyTwoCoinTurnstile extends TwoCoinTurnstile implements TurnstileActions {
9 | String output;
10 | MyTwoCoinTurnstile({this.output = ''});
11 |
12 | lock() {
13 | output += 'L';
14 | }
15 |
16 | unlock() {
17 | output += 'U';
18 | }
19 |
20 | thankyou() {
21 | output += 'T';
22 | }
23 |
24 | alarmOn() {
25 | output += 'A';
26 | }
27 |
28 | alarmOff() {
29 | output += 'O';
30 | }
31 |
32 | unexpected_transition(final String state, final String event) {
33 | output += 'X($state,$event)';
34 | }
35 | }
36 |
37 | main() {
38 | MyTwoCoinTurnstile fsm;
39 |
40 | setUp(() {
41 | fsm = MyTwoCoinTurnstile();
42 | });
43 |
44 | test('NormalBehavior', () {
45 | fsm.Coin();
46 | fsm.Coin();
47 | fsm.Pass();
48 | expect(fsm.output, equals('UL'));
49 | });
50 |
51 | test('Alarm', () {
52 | fsm.Pass();
53 | expect(fsm.output, equals('A'));
54 | });
55 |
56 | test('Thankyou', () {
57 | fsm.Coin();
58 | fsm.Coin();
59 | fsm.Coin();
60 | expect(fsm.output, equals('UT'));
61 | });
62 |
63 | test('NormalManyThanksAndAlarm', () {
64 | fsm.Coin();
65 | fsm.Coin();
66 | fsm.Pass();
67 | fsm.Coin();
68 | fsm.Coin();
69 | fsm.Coin();
70 | fsm.Pass();
71 | fsm.Pass();
72 | expect(fsm.output, equals('ULUTLA'));
73 | });
74 |
75 | test('Undefined', () {
76 | fsm.Pass();
77 | fsm.Pass();
78 | expect(fsm.output, equals('AX(Alarming,Pass)'));
79 | });
80 |
81 | test('Reset', () {
82 | fsm.Pass();
83 | fsm.Reset();
84 | expect(fsm.output, equals('AOL'));
85 | });
86 | }
87 |
--------------------------------------------------------------------------------
/test_cases/dart_turnstile/twoCoinTurnstile.sm:
--------------------------------------------------------------------------------
1 | Actions: TurnstileActions
2 | FSM: TwoCoinTurnstile
3 | Initial: Locked
4 | {
5 | (Base) Reset Locked lock
6 |
7 | Locked : Base {
8 | Pass Alarming -
9 | Coin FirstCoin -
10 | }
11 |
12 | Alarming : Base alarmOff {
13 | - - -
14 | }
15 |
16 | FirstCoin : Base {
17 | Pass Alarming -
18 | Coin Unlocked unlock
19 | }
20 |
21 | Unlocked : Base {
22 | Pass Locked lock
23 | Coin - thankyou
24 | }
25 | }
--------------------------------------------------------------------------------
/test_cases/go_turnstile/Makefile:
--------------------------------------------------------------------------------
1 | all : clean two_coin_turnstile.go turnstile_test.go
2 | go vet .
3 | go test .
4 |
5 | clean :
6 | rm -f two_coin_turnstile.go
7 |
8 | two_coin_turnstile.go : two_coin_turnstile.sm
9 | java -jar ../../build/jar/smc.jar -l Go $?
10 | go fmt $@
11 |
12 |
13 |
--------------------------------------------------------------------------------
/test_cases/go_turnstile/turnstile_actions.go:
--------------------------------------------------------------------------------
1 | package twocointurnstile
2 |
3 | // TurnstileActions represents the possible actions that can be performed.
4 | type TurnstileActions interface {
5 | Lock()
6 | Unlock()
7 | AlarmOn()
8 | AlarmOff()
9 | Thankyou()
10 | UnexpectedTransition(state, event string)
11 | }
12 |
--------------------------------------------------------------------------------
/test_cases/go_turnstile/turnstile_test.go:
--------------------------------------------------------------------------------
1 | package twocointurnstile
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | var _ TurnstileActions = &MyTwoCoinTurnstile{}
9 |
10 | // MyTwoCoinTurnstile implements the TurnstileActions interface.
11 | type MyTwoCoinTurnstile struct {
12 | output string
13 | t *testing.T
14 | }
15 |
16 | func (m *MyTwoCoinTurnstile) Lock() {
17 | m.output += "L"
18 | }
19 |
20 | func (m *MyTwoCoinTurnstile) Unlock() {
21 | m.output += "U"
22 | }
23 |
24 | func (m *MyTwoCoinTurnstile) Thankyou() {
25 | m.output += "T"
26 | }
27 |
28 | func (m *MyTwoCoinTurnstile) AlarmOn() {
29 | m.output += "A"
30 | }
31 |
32 | func (m *MyTwoCoinTurnstile) AlarmOff() {
33 | m.output += "O"
34 | }
35 |
36 | func (m *MyTwoCoinTurnstile) UnexpectedTransition(state, event string) {
37 | e := fmt.Sprintf("X(%v,%v)", state, event)
38 | m.output += e
39 | }
40 |
41 | func (m *MyTwoCoinTurnstile) check(function, want string) {
42 | m.t.Helper()
43 |
44 | if m.output != want {
45 | m.t.Errorf("%s failed: got %v, want %v", function, m.output, want)
46 | }
47 | }
48 |
49 | func setup(t *testing.T) (*TwoCoinTurnstile, *MyTwoCoinTurnstile) {
50 | m := &MyTwoCoinTurnstile{t: t}
51 | fsm := New(m)
52 | return fsm, m
53 | }
54 |
55 | func TestNormalBehavior(t *testing.T) {
56 | fsm, m := setup(t)
57 | fsm.Coin()
58 | fsm.Coin()
59 | fsm.Pass()
60 | m.check("testNormalBehavior", "UL")
61 | }
62 |
63 | func TestAlarm(t *testing.T) {
64 | fsm, m := setup(t)
65 | fsm.Pass()
66 | m.check("testAlarm", "A")
67 | }
68 |
69 | func TestThankyou(t *testing.T) {
70 | fsm, m := setup(t)
71 | fsm.Coin()
72 | fsm.Coin()
73 | fsm.Coin()
74 | m.check("testThankyou", "UT")
75 | }
76 |
77 | func TestNormalManyThanksAndAlarm(t *testing.T) {
78 | fsm, m := setup(t)
79 | fsm.Coin()
80 | fsm.Coin()
81 | fsm.Pass()
82 | fsm.Coin()
83 | fsm.Coin()
84 | fsm.Coin()
85 | fsm.Pass()
86 | fsm.Pass()
87 | m.check("testNormalManyThanksAndAlarm", "ULUTLA")
88 | }
89 |
90 | func TestUndefined(t *testing.T) {
91 | fsm, m := setup(t)
92 | fsm.Pass()
93 | fsm.Pass()
94 | m.check("testUndefined", "AX(Alarming,Pass)")
95 | }
96 |
97 | func TestReset(t *testing.T) {
98 | fsm, m := setup(t)
99 | fsm.Pass()
100 | fsm.Reset()
101 | m.check("testReset", "AOL")
102 | }
103 |
--------------------------------------------------------------------------------
/test_cases/go_turnstile/two_coin_turnstile.sm:
--------------------------------------------------------------------------------
1 | Actions: TurnstileActions
2 | FSM: TwoCoinTurnstile
3 | Initial: Locked
4 | {
5 | (Base) Reset Locked lock
6 |
7 | Locked : Base {
8 | Pass Alarming -
9 | Coin FirstCoin -
10 | }
11 |
12 | Alarming : Base alarmOff {
13 | - - -
14 | }
15 |
16 | FirstCoin : Base {
17 | Pass Alarming -
18 | Coin Unlocked unlock
19 | }
20 |
21 | Unlocked : Base {
22 | Pass Locked lock
23 | Coin - thankyou
24 | }
25 | }
--------------------------------------------------------------------------------
/test_cases/java_turnstile/.idea/.name:
--------------------------------------------------------------------------------
1 | NSCTurnstile
--------------------------------------------------------------------------------
/test_cases/java_turnstile/NSCTurnstile.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/test_cases/java_turnstile/build.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/test_cases/java_turnstile/src/TurnstileActions.java:
--------------------------------------------------------------------------------
1 | public interface TurnstileActions {
2 | void unlock();
3 | void alarmOn();
4 | void thankyou();
5 | void lock();
6 | void alarmOff();
7 | }
8 |
--------------------------------------------------------------------------------
/test_cases/java_turnstile/src/TwoCoinTurnstileTest.java:
--------------------------------------------------------------------------------
1 | import org.junit.Before;
2 | import org.junit.Test;
3 |
4 | import static org.hamcrest.CoreMatchers.is;
5 | import static org.junit.Assert.assertThat;
6 |
7 | public class TwoCoinTurnstileTest {
8 | private String output = "";
9 | private TwoCoinTurnstile sm;
10 |
11 | class TwoCoinTurnstileImp extends TwoCoinTurnstile {
12 | public void unhandledTransition(String state, String event) {
13 | output += String.format("X(%s,%s) ", state, event);
14 | }
15 |
16 | public void thankyou() {
17 | output += "T";
18 | }
19 |
20 | public void unlock() {
21 | output += "U";
22 | }
23 |
24 | public void alarmOn() {
25 | output += "A";
26 | }
27 |
28 | public void lock() {
29 | output += "L";
30 | }
31 |
32 | public void alarmOff() {
33 | output += "O";
34 | }
35 | }
36 |
37 | @Before
38 | public void setup() {
39 | output = "";
40 | sm = new TwoCoinTurnstileImp();
41 | }
42 |
43 | @Test
44 | public void normal() throws Exception {
45 | sm.Coin();
46 | sm.Coin();
47 | sm.Pass();
48 | assertThat(output, is("UL"));
49 | }
50 |
51 | @Test
52 | public void oneCoinAttempt() throws Exception {
53 | sm.Coin();
54 | sm.Pass();
55 | assertThat(output, is("A"));
56 | }
57 |
58 | @Test
59 | public void alarmReset() throws Exception {
60 | sm.Pass();
61 | sm.Reset();
62 | assertThat(output, is("AOL"));
63 | }
64 |
65 | @Test
66 | public void extraCoins() throws Exception {
67 | sm.Coin();
68 | sm.Coin();
69 | sm.Coin();
70 | sm.Coin();
71 | sm.Pass();
72 | assertThat(output, is("UTTL"));
73 | }
74 |
75 | @Test
76 | public void pass() throws Exception {
77 |
78 | }
79 |
80 |
81 | }
--------------------------------------------------------------------------------
/test_cases/java_turnstile/src/twoCoinTurnstile.sm:
--------------------------------------------------------------------------------
1 | FSM: TwoCoinTurnstile
2 | Initial: Locked
3 | Actions: TurnstileActions
4 | {
5 | (Base) Reset Locked lock
6 |
7 | Locked : Base {
8 | Pass Alarming -
9 | Coin FirstCoin -
10 | }
11 |
12 | Alarming : Base alarmOff {
13 | - - -
14 | }
15 |
16 | FirstCoin : Base {
17 | Pass Alarming -
18 | Coin Unlocked unlock
19 | }
20 |
21 | Unlocked : Base {
22 | Pass Locked lock
23 | Coin - thankyou
24 | }
25 | }
--------------------------------------------------------------------------------
/test_cases/simple turnstile/turnstile.sm:
--------------------------------------------------------------------------------
1 | Initial: Locked
2 | FSM: Turnstile
3 | {
4 | Locked Coin Unlocked unlock
5 | Locked Pass Locked alarm
6 | Unlocked Coin Unlocked thankyou
7 | Unlocked Pass Locked lock
8 | }
9 |
10 |
11 |
--------------------------------------------------------------------------------