mirror of
https://github.com/bluekitchen/btstack.git
synced 2024-12-29 09:26:08 +00:00
started state machine extraction tool
This commit is contained in:
parent
fc3ea481fb
commit
588b0e6cca
31
docs/StateMachineExtraction.txt
Normal file
31
docs/StateMachineExtraction.txt
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
104
docs/extract.py
Executable file
104
docs/extract.py
Executable file
@ -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 "}"
|
||||
|
54
docs/lamp.c
Normal file
54
docs/lamp.c
Normal file
@ -0,0 +1,54 @@
|
||||
#include <stdio.h>
|
||||
|
||||
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 );
|
||||
}
|
Loading…
Reference in New Issue
Block a user