Created
December 28, 2012 15:49
-
-
Save monperrus/4399016 to your computer and use it in GitHub Desktop.
Contains a small model-driven development toolchain for teaching purpose
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# -*- coding: iso-8859-1 -*- | |
# Contains a small model-driven development toolchain for teaching purpose | |
# * a state machine metamodel | |
# * a code generator state machine -> C code | |
# * an EMf-like marshalling system based on YAML | |
# | |
# It does not use any model-driven library in order to focus on concepts | |
# and not on the accidental complexity of tools. | |
# | |
# (c) Martin Monperrus, 2010 | |
# Creative Commons Attribution License | |
############# Metamodel ########### | |
class Metamodel: | |
class FSM: | |
def __init__(self): | |
# a list of states | |
self.lstates=[] | |
class State: | |
def __init__(self): | |
# a list of transitions | |
self.transitions=[] | |
# a name | |
self.name='unset' | |
class Transition: | |
def __init__(self): | |
# the target state | |
self.tostate = None | |
# a char triggering the transition | |
self.trigger=None | |
# a string to be displayed when the transition is selected | |
self.message=None | |
############# Code Generator ########### | |
def fsm2C(fsm,cfile,printdebug): | |
""" This is a code generator from FSM to C | |
@param fsm a instance of FSM | |
@param cfile a file name (string) | |
@param printdebug is a boolean to configure the generation, | |
it specifies whether the generated code prints some debug information """ | |
if not isinstance(fsm,Metamodel.FSM): | |
raise 'fsm is not an instance of FSM!' | |
print "creating "+cfile+"..." | |
print "gcc -o fsm.exe "+cfile | |
f = open(cfile,'w') | |
f.write('#include <stdio.h>\n#include <stddef.h>\n'); | |
i=0 | |
for s in fsm.lstates: | |
f.write('#define '+str(s.name)+' '+str(i)+"\n") | |
i=i+1 | |
f.write('int main(int argc, char *argv[]) {') | |
if printdebug: f.write('printf("Starting...\\n");') | |
# f.write('if (argc == 1) { printf("ERROR : input messages as argv[1] needed\\n"); return 1;}') | |
f.write('\n//char* l=argv[1];\nlong state = 0;char message;\n') | |
f.write('do {') | |
f.write('message = getchar();\n') | |
if printdebug: | |
f.write('printf("Reading message : %c\\n",message);\n') | |
f.write('switch (state) {') | |
for s in fsm.lstates: | |
f.write('case '+str(s.name)+': ') | |
f.write('switch (message) {') | |
for t in s.transitions: | |
f.write('case \''+chr(ord(t.trigger))+'\': state = '+str(t.tostate)+';\n') | |
if printdebug: | |
f.write('printf("** transition matched **: entering in '+str(t.tostate)+'\\n");') | |
f.write('printf("-> '+t.message+'\\n");\n') | |
f.write('break;\n') | |
f.write('default: ') | |
if printdebug: | |
f.write('printf("No transitions for this input message.\\n");\n') | |
f.write('break;}break;\n') | |
f.write('default: break;\n') | |
f.write('}}\n') | |
f.write('while (message !=EOF);') | |
if printdebug: | |
f.write('printf("Ending...\\n");') | |
f.write('return 0;}\n') | |
f.close() | |
############# Marshalling System ########### | |
import yaml | |
class MarshallingSystem: | |
""" adds some helper functions to the metamodel classes: | |
* setters | |
* YAML serialization | |
""" | |
class FSM(Metamodel.FSM,yaml.YAMLObject): | |
yaml_tag='fsm' | |
def addState(self,state): | |
self.lstates.append(state) | |
class State(Metamodel.State,yaml.YAMLObject): | |
yaml_tag='state' | |
def __str__(self): | |
return self.name | |
def __init__(self): | |
super(MarshallingSystem.State,self).__init__() | |
import random | |
self.name = 's'+str(int(random.random()*1000)) | |
def addTransition(self,transition): | |
self.transitions.append(transition) | |
class Transition(Metamodel.Transition,yaml.YAMLObject): | |
yaml_tag='transition' | |
############# Factory ########### | |
class Factory: | |
""" encapsulates the use of the correct classes """ | |
def createFSM(self): | |
return MarshallingSystem.FSM() | |
def createState(self): | |
return MarshallingSystem.State() | |
def createTransition(self): | |
return MarshallingSystem.Transition() | |
############# Imperative Definition of the Model ########### | |
fsm = Factory().createFSM() | |
s1 = Factory().createState() | |
s2 = Factory().createState() | |
fsm.addState(s1) | |
fsm.addState(s2) | |
t1 = Factory().createTransition() | |
t1.trigger='x' | |
t1.message='yeahhh' | |
t1.tostate=s2 | |
t2 = Factory().createTransition() | |
t2.trigger='y' | |
t2.message='cool' | |
t2.tostate=s1 | |
s1.addTransition(t1) | |
s2.addTransition(t2) | |
fsm2C(fsm,'output.c',False) # code generation | |
############# Declarative Definition of the Model using the Marshalling System ########### | |
model="""!<fsm> | |
lstates: | |
- !<state> | |
name: s74 | |
transitions: | |
- !<transition> | |
trigger: z | |
message: oouap | |
tostate: s75 | |
- !<state> | |
name: s75 | |
transitions: | |
- !<transition> | |
trigger: l | |
message: slulll | |
tostate: s74 | |
""" | |
from StringIO import StringIO | |
# stream = file('model.yaml', 'r') | |
stream = StringIO() # emulating loading from file | |
stream.write(model) | |
stream.seek(0) # back to the beginning | |
fsm2 = yaml.load(stream) | |
fsm2C(fsm2,'output2.c',False) # code generation | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment