├── .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 | --------------------------------------------------------------------------------