From 588b0e6ccaa5406fcaa4e775f6985fe4e07e1233 Mon Sep 17 00:00:00 2001 From: "matthias.ringwald" Date: Sat, 25 Jun 2011 18:17:42 +0000 Subject: [PATCH] started state machine extraction tool --- docs/StateMachineExtraction.txt | 31 ++++++++++ docs/extract.py | 104 ++++++++++++++++++++++++++++++++ docs/lamp.c | 54 +++++++++++++++++ 3 files changed, 189 insertions(+) create mode 100644 docs/StateMachineExtraction.txt create mode 100755 docs/extract.py create mode 100644 docs/lamp.c diff --git a/docs/StateMachineExtraction.txt b/docs/StateMachineExtraction.txt new file mode 100644 index 000000000..e1e184d8e --- /dev/null +++ b/docs/StateMachineExtraction.txt @@ -0,0 +1,31 @@ + +Project title: "Annotation-based state machine definition for doubly nested switch-case implementations" + +- goals: testability, documentation +- assumptions: fixed code stucture +- input: some code (.c) +- output: graphviz state machine ... +- documentation: BTstack Wiki page +- implementation: Python +- problems: + - implicit event like "can send packet now", =? deal with guards? + - "inverse handler" for "global" events + +Example: +{ + // @STATEMACHINE(multiplexer) + switch (multiplexer->state) { // detect state variable, count { + case W4_MULTIPLEXER: // implicit state + switch (event) { // events start here + case L2CAP_OPEN: // implicit event + // @ACTION(action description) + multiplexer->state = OTHER_STATE; + break; // break || return -> end case block + } + break; + case OTHER_STATE: + break; + } + } +} + diff --git a/docs/extract.py b/docs/extract.py new file mode 100755 index 000000000..5735e3d3e --- /dev/null +++ b/docs/extract.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python + +import re + +class Transition(object): + def __init__ (self, fromState, toState, event, guard, action): + self.fromState = fromState + self.toState = toState + self.event = event + self.guard = guard + self.action = action + +statemachineName = "" +stateVariable = "" +numBraces = 0 +fromState = "" +toState = "" +onEvent = "" +action = "" + +def parseLine(line): + global statemachineName + global numBraces + global stateVariable + global fromState + global toState + global onEvent + global action + + # looks for annotations + ann = re.compile("(@\w*)\s*(\((.*)\))?") + anns = ann.findall(line) + if anns: + if "@STATEMACHINE" in anns[0]: + # print "statemachine, name:", anns[0][2] + statemachineName = anns[0][2] + numBraces = 0 + stateVariable = "" + if "@ACTION" in anns[0]: + action = anns[0][2] + + # look for switch + switch = re.compile("(switch)\s*\(\s*(.*)\s*\)") + switches = switch.findall(line) + if (switches): + if numBraces == 0: + stateVariable = switches[0][1] + # print "switch on state opened", numBraces, stateVariable + # if numBraces == 1: + # print "switch on event opened", numBraces + + # count curly braces when inside statemachine + if statemachineName: + for c in line: + before = numBraces + if '{' in c: + numBraces = numBraces + 1 + if '}' in c: + numBraces = numBraces - 1 + + # look for case + case = re.compile("(case)\s*(.*):") + cases = case.findall(line) + if cases: + if numBraces == 1: + # print "from state: " + cases[0][1] + fromState = cases[0][1] + if numBraces == 2: + # print "on event: " + cases[0][1] + onEvent = cases[0][1] + toState = "" + action = "" + + # look for break or return + brk = re.compile("(break)") + breaks = brk.findall(line) + rtrn = re.compile("(return)") + returns = rtrn.findall(line) + if numBraces > 1: + if breaks or returns: + if not toState: + toState = fromState + # print "to state:", toState + print fromState + "->" + toState + " [label=\"" + onEvent + "/" + action + "\"];" + + # look for state transition + if stateVariable: + stateTransition = re.compile( stateVariable + "\s*=\s*(.*);") + transition = stateTransition.findall(line) + if transition: + toState = transition[0] + + +def parseFile(filename): + f = open (filename, "r") + for line in f: + parseLine(line) + f.close() + +print "digraph fsm { " +print "size = \"8.5\"" +parseFile("/Projects/iPhone/btstack/src/l2cap.c") +print "}" + diff --git a/docs/lamp.c b/docs/lamp.c new file mode 100644 index 000000000..d1b3362c4 --- /dev/null +++ b/docs/lamp.c @@ -0,0 +1,54 @@ +#include + +typedef enum { + LAMP_OFF = 1, + LAMP_ON +} state_t; + +typedef enum { + TOGGLE_ON = 1, + TOGGLE_OFF +} event_t; + +state_t state; + +void statemachine(event_t ev){ + + printf("State: %u, event: %u\n", state, ev); + + // @STATEMACHINE(lamp) + switch(state){ + case LAMP_OFF: + switch(ev){ + case TOGGLE_ON: + // @ACTION(Turning lamp on) + printf("Turning lamp on\n"); + state = LAMP_ON; + break; + case TOGGLE_OFF: + // @ACTION(Ignoring toggle off) + printf("Ignoring toggle off\n"); + break; + } + break; + case LAMP_ON: + switch(ev){ + case TOGGLE_ON: + // @ACTION(Ignoring toggle on) + printf("Ignoring toggle on\n"); + break; + case TOGGLE_OFF: + // @ACTION(Turning lamp off) + printf("Turning lamp off\n"); + state = LAMP_OFF; + break; + } + } +} + +int main(void){ + state = LAMP_OFF; + statemachine( TOGGLE_ON ); + statemachine( TOGGLE_ON ); + statemachine( TOGGLE_OFF ); +}