Skip to content

Instantly share code, notes, and snippets.

@monperrus
Created December 28, 2012 15:49
Show Gist options
  • Save monperrus/4399016 to your computer and use it in GitHub Desktop.
Save monperrus/4399016 to your computer and use it in GitHub Desktop.
Contains a small model-driven development toolchain for teaching purpose
#!/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