summaryrefslogtreecommitdiffhomepage
path: root/tools
diff options
context:
space:
mode:
authorNicolas Schodet2008-03-17 22:53:01 +0100
committerNicolas Schodet2008-03-17 22:53:01 +0100
commit164ac3a34cbac441e82b256c97cb8784ea9d482c (patch)
treeb0db1276083d168b50e6aa2be9621368a36184cd /tools
parent388a90600023cca7d7b28702fa4cc75ed5074123 (diff)
* tools/dfagen:
- added dfagen.
Diffstat (limited to 'tools')
-rw-r--r--tools/dfagen/dfagen.py3
-rw-r--r--tools/dfagen/dfagen/__init__.py0
-rw-r--r--tools/dfagen/dfagen/automaton.py111
-rw-r--r--tools/dfagen/dfagen/command.py23
-rw-r--r--tools/dfagen/dfagen/output/__init__.py24
-rw-r--r--tools/dfagen/dfagen/output/c.py154
-rw-r--r--tools/dfagen/dfagen/output/c/template.c39
-rw-r--r--tools/dfagen/dfagen/output/c/template.h59
-rw-r--r--tools/dfagen/dfagen/output/c/template_cb.h12
-rw-r--r--tools/dfagen/dfagen/output/c/template_cb_decl.h6
-rw-r--r--tools/dfagen/dfagen/output/c/template_cb_impl.c9
-rw-r--r--tools/dfagen/dfagen/output/c/template_cb_skel.c9
-rw-r--r--tools/dfagen/dfagen/output/dot.py19
-rw-r--r--tools/dfagen/dfagen/parser.g62
-rw-r--r--tools/dfagen/doc/dfagen.txt181
-rw-r--r--tools/dfagen/examples/Makefile40
-rw-r--r--tools/dfagen/examples/ex1.conf5
-rw-r--r--tools/dfagen/examples/ex1.fsm21
-rw-r--r--tools/dfagen/examples/ex1_cb.c.patch41
-rw-r--r--tools/dfagen/examples/ex2.conf5
-rw-r--r--tools/dfagen/examples/ex2.fsm40
-rw-r--r--tools/dfagen/examples/ex2_cb.c.patch67
-rw-r--r--tools/dfagen/examples/ex2_robot.c39
-rw-r--r--tools/dfagen/examples/ex2_robot.h18
24 files changed, 987 insertions, 0 deletions
diff --git a/tools/dfagen/dfagen.py b/tools/dfagen/dfagen.py
new file mode 100644
index 00000000..a65506bb
--- /dev/null
+++ b/tools/dfagen/dfagen.py
@@ -0,0 +1,3 @@
+from dfagen.command import run
+
+run ()
diff --git a/tools/dfagen/dfagen/__init__.py b/tools/dfagen/dfagen/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tools/dfagen/dfagen/__init__.py
diff --git a/tools/dfagen/dfagen/automaton.py b/tools/dfagen/dfagen/automaton.py
new file mode 100644
index 00000000..367da582
--- /dev/null
+++ b/tools/dfagen/dfagen/automaton.py
@@ -0,0 +1,111 @@
+class Event:
+ """Event definition."""
+
+ def __init__ (self, name, comments = ''):
+ self.name = name
+ self.comments = comments
+ pass
+
+ def __str__ (self):
+ s = ' ' + self.name + '\n'
+ if self.comments:
+ s += ' ' + self.comments.replace ('\n', '\n ') + '\n'
+ return s
+
+class State:
+ """State definition."""
+
+ def __init__ (self, name, comments = ''):
+ self.name = name
+ self.comments = comments
+ self.transitions = { }
+
+ def __str__ (self):
+ s = ' ' + self.name + '\n'
+ if self.comments:
+ s += ' ' + self.comments.replace ('\n', '\n ') + '\n'
+ return s
+
+ def add_branch (self, branch):
+ if branch.event not in self.transitions:
+ self.transitions[branch.event] = Transition (branch.event)
+ self.transitions[branch.event].add_branch (branch)
+
+class Transition:
+ """Transition definition."""
+
+ def __init__ (self, event):
+ self.event = event
+ self.branches = { }
+
+ def add_branch (self, branch):
+ assert self.event is branch.event
+ if branch.name == None and self.branches:
+ raise KeyError (branch.name)
+ if branch.name != None and None in self.branches:
+ raise KeyError (branch.name)
+ if branch.name in self.branches:
+ raise KeyError (branch.name)
+ self.branches[branch.name] = branch
+
+ def __str__ (self):
+ s = ''
+ for br in self.branches.values ():
+ s += str (br);
+ return s
+
+class TransitionBranch:
+
+ def __init__ (self, event, name = None, to = None, comments = ''):
+ self.event = event
+ self.name = name
+ self.to = to
+ self.comments = comments
+
+ def __str__ (self):
+ s = ' ' + self.event.name
+ if self.name:
+ s += ': ' + self.name
+ s += ' -> ' + (self.to and self.to.name or '.') + '\n'
+ if self.comments:
+ s += ' ' + self.comments.replace ('\n', '\n ') + '\n'
+ return s
+
+class Automaton:
+
+ def __init__ (self, name):
+ self.name = name
+ self.comments = ''
+ self.initial = None
+ self.states = { }
+ self.events = { }
+
+ def add_state (self, state):
+ if state.name in self.states:
+ raise KeyError (state.name)
+ if not self.states:
+ self.initial = state
+ self.states[state.name] = state
+
+ def add_event (self, event):
+ if event.name in self.events:
+ raise KeyError (event.name)
+ self.events[event.name] = event
+
+ def __str__ (self):
+ s = self.name
+ if self.comments:
+ s += ' ' + self.comments.replace ('\n', '\n ') + '\n'
+ s += '\nStates:\n'
+ for state in self.states.values ():
+ s += str (state)
+ s += '\nEvents:\n'
+ for event in self.events.values ():
+ s += str (event)
+ s += '\n'
+ for state in self.states.values ():
+ s += state.name + ':\n'
+ for tr in state.transitions.values ():
+ s += str (tr)
+ s += '\n'
+ return s
diff --git a/tools/dfagen/dfagen/command.py b/tools/dfagen/dfagen/command.py
new file mode 100644
index 00000000..d384087e
--- /dev/null
+++ b/tools/dfagen/dfagen/command.py
@@ -0,0 +1,23 @@
+import dfagen.parser, dfagen.output
+from optparse import OptionParser
+
+def run ():
+ opt = OptionParser ()
+ opt.add_option ('-d', '--dfa', dest='dfa',
+ help='read DFA description from FILE', metavar='FILE')
+ opt.add_option ('-o', '--output', dest='output',
+ help='choose output format', metavar='OUTPUT')
+ opt.add_option ('-c', '--config', dest='config',
+ help='read output configuration from FILE', metavar='FILE')
+ opt.add_option ('-p', '--prefix', dest='prefix',
+ help='use PREFIX for generated output', metavar='PREFIX')
+ # TODO add more error checking.
+ (options, args) = opt.parse_args ()
+ # Read automaton.
+ f = open (options.dfa, 'r')
+ a = dfagen.parser.parse ('automaton', f.read ())
+ f.close ()
+ # Read config.
+ cfg = dfagen.output.UserConfig (options.config)
+ # Produce output.
+ dfagen.output.get_output (options.output).write (options.prefix, a, cfg)
diff --git a/tools/dfagen/dfagen/output/__init__.py b/tools/dfagen/dfagen/output/__init__.py
new file mode 100644
index 00000000..be71aef7
--- /dev/null
+++ b/tools/dfagen/dfagen/output/__init__.py
@@ -0,0 +1,24 @@
+from ConfigParser import ConfigParser
+
+class UserConfig:
+ def __init__ (self, file):
+ if file:
+ f = open (file, 'r')
+ cp = ConfigParser ()
+ cp.readfp (f)
+ f.close ()
+ self.dict = dict (cp.items ('user'))
+
+ def __getitem__ (self, key):
+ return self.dict[key]
+
+import c
+import dot
+
+outputs = dict (
+ c = c,
+ dot = dot,
+ )
+
+def get_output (name):
+ return outputs[name];
diff --git a/tools/dfagen/dfagen/output/c.py b/tools/dfagen/dfagen/output/c.py
new file mode 100644
index 00000000..e19ca713
--- /dev/null
+++ b/tools/dfagen/dfagen/output/c.py
@@ -0,0 +1,154 @@
+import os.path
+
+class WriterData:
+
+ def __init__ (self, prefix, automaton, user):
+ self.prefix = prefix
+ self.automaton = automaton
+ self.user = user
+ self.states = self.automaton.states.values ()
+ self.events = self.automaton.events.values ()
+ self.dict = dict (
+ prefix = prefix,
+ PREFIX = prefix.upper (),
+ name = automaton.name,
+ comments = automaton.comments,
+ initial = automaton.initial.name,
+ states = self.list_states,
+ events = self.list_events,
+ branches = self.list_branches,
+ transition_table = self.transition_table,
+ cb_impl = self.cb_impl,
+ cb_decl = self.cb_decl,
+ )
+
+ def list_states (self):
+ return ''.join ([' ' + self.prefix.upper () + '_STATE_' + s.name
+ + ',\n' for s in self.states])
+
+ def list_events (self):
+ return ''.join ([' ' + self.prefix.upper () + '_EVENT_'
+ + e.name.replace (' ', '_') + ',\n' for e in self.events])
+
+ def list_branches (self):
+ l = ''
+ for s in self.states:
+ for tr in s.transitions.values ():
+ for br in tr.branches.values ():
+ n = dict (
+ PREFIX = self.prefix.upper (),
+ state = s.name,
+ event = tr.event.name.replace (' ', '_'),
+ branch = (br.name and br.name.replace (' ', '_')
+ or ''),
+ to = (br.to and br.to.name or s.name),
+ )
+ l += (' %(PREFIX)s_BRANCH__%(state)s__%(event)s__%(branch)s = '
+ + '_BRANCH (%(state)s, %(event)s, %(to)s),\n') % n
+ return l
+
+ def transition_table (self):
+ r = ''
+ for s in self.states:
+ r += ' { '
+ es = [ ]
+ for e in self.events:
+ if e in s.transitions:
+ es.append (self.prefix + '__' + s.name + '__'
+ + e.name.replace (' ', '_'))
+ else:
+ es.append ('NULL')
+ r += ',\n '.join (es)
+ r += ' },\n'
+ return r
+
+ def states_template (self, template):
+ t = open (os.path.join (self.templatedir, template), 'r')
+ tt = t.read ()
+ t.close ()
+ exp = ''
+ for s in self.states:
+ for tr in s.transitions.values ():
+ d = WriterData (self.prefix, self.automaton, self.user)
+ branches_to = '\n'.join (
+ [(br.name and br.name or '')
+ + ' => '
+ + (br.to and br.to.name or s.name)
+ + (br.comments and ('\n '
+ + br.comments.replace ('\n', '\n ')) or '')
+ for br in tr.branches.values ()])
+ returns = '\n'.join (
+ [' return ' + self.prefix + '_next'
+ + (br.name and '_branch' or '')
+ + ' (' + s.name + ', '
+ + tr.event.name.replace (' ', '_')
+ + (br.name and ', ' + br.name.replace (' ', '_')
+ or '')
+ + ');'
+ for br in tr.branches.values ()])
+ d.dict = dict (
+ prefix = self.prefix,
+ user = self.user,
+ state = s.name,
+ event = tr.event.name.replace (' ', '_'),
+ branches_to = branches_to,
+ returns = returns,
+ )
+ exp += tt % d
+ return exp
+
+ def cb_impl (self):
+ return self.states_template ('template_cb_impl.c')
+
+ def cb_decl (self):
+ return self.states_template ('template_cb_decl.h')
+
+ def __getitem__ (self, key):
+ preproc = lambda v: v
+ if key.startswith ('*'):
+ key = key[1:]
+ preproc = lambda v: ' * ' + v.replace ('\n', '\n * ') + '\n'
+ if key.startswith ('_'):
+ key = key[1:]
+ preproc = lambda v: v and v + '\n' or ''
+ val = None
+ if key in self.dict:
+ try:
+ val = self.dict[key] ()
+ except TypeError:
+ val = self.dict[key]
+ elif key.startswith ('user.'):
+ val = self.user[key[5:]]
+ val = preproc (val)
+ if val is None:
+ raise KeyError, key
+ return val
+
+class Writer:
+
+ def __init__ (self, data, templatedir):
+ data.templatedir = templatedir
+ self.data = data
+ self.templatedir = templatedir
+
+ def write_template (self, template, output):
+ t = open (os.path.join (self.templatedir, template), 'r')
+ tt = t.read ()
+ t.close ()
+ exp = tt % self.data
+ o = open (output, 'w')
+ o.write (exp)
+ o.close ()
+
+ def write (self):
+ self.write_template ('template.h', self.data.prefix + '.h')
+ self.write_template ('template.c', self.data.prefix + '.c')
+ self.write_template ('template_cb.h', self.data.prefix + '_cb.h')
+ self.write_template ('template_cb_skel.c',
+ self.data.prefix + '_cb_skel.c')
+
+def write (prefix, automaton, user):
+ w = Writer (WriterData (prefix, automaton, user),
+ os.path.splitext (__file__)[0])
+ w.write ()
+
diff --git a/tools/dfagen/dfagen/output/c/template.c b/tools/dfagen/dfagen/output/c/template.c
new file mode 100644
index 00000000..a8679046
--- /dev/null
+++ b/tools/dfagen/dfagen/output/c/template.c
@@ -0,0 +1,39 @@
+/*
+ * THIS IS AN AUTOMATICALLY GENERATED FILE, DO NOT EDIT!
+ *
+ * %(name)s
+ *
+%(*comments)s */
+#include "%(prefix)s_cb.h"
+#include <stddef.h>
+#include <assert.h>
+
+%(_user.type-decl)s
+/* %(name)s transition table. */
+static const %(prefix)s_transition_t
+%(prefix)s_transition_table[%(PREFIX)s_STATE_NB][%(PREFIX)s_EVENT_NB] = {
+%(transition_table)s};
+
+/* Initialise %(name)s automaton. */
+void
+%(prefix)s_init (%(user.type)s *user)
+{
+ user->%(user.field)s = %(PREFIX)s_STATE_%(initial)s;
+}
+
+/* Handle events on %(name)s automaton. */
+void
+%(prefix)s_handle_event (%(user.type)s *user, %(prefix)s_event_t event)
+{
+ assert (user);
+ %(prefix)s_state_t state = user->%(user.field)s;
+ assert (state < %(PREFIX)s_STATE_NB);
+ assert (event < %(PREFIX)s_EVENT_NB);
+ %(prefix)s_transition_t tr = %(prefix)s_transition_table[state][event];
+ assert (tr);
+ %(prefix)s_branch_t br = tr (user);
+ assert (((br >> 16) & 0xff) == state);
+ assert (((br >> 8) & 0xff) == event);
+ user->%(user.field)s = br & 0xff;
+}
+
diff --git a/tools/dfagen/dfagen/output/c/template.h b/tools/dfagen/dfagen/output/c/template.h
new file mode 100644
index 00000000..2be89f31
--- /dev/null
+++ b/tools/dfagen/dfagen/output/c/template.h
@@ -0,0 +1,59 @@
+#ifndef %(prefix)s_h
+#define %(prefix)s_h
+/*
+ * THIS IS AN AUTOMATICALLY GENERATED FILE, DO NOT EDIT!
+ *
+ * %(name)s
+ *
+%(*comments)s */
+
+%(_user.type-forward-decl)s
+/* %(name)s states. */
+enum %(prefix)s_state_t
+{
+%(states)s %(PREFIX)s_STATE_NB
+};
+typedef enum %(prefix)s_state_t %(prefix)s_state_t;
+
+/* %(name)s events. */
+enum %(prefix)s_event_t
+{
+%(events)s %(PREFIX)s_EVENT_NB
+};
+typedef enum %(prefix)s_event_t %(prefix)s_event_t;
+
+/* This macro enables checks for branches used in the wrong state/event
+ * combination. */
+#define _BRANCH(state, event, to) \
+ ((%(PREFIX)s_STATE_ ## state) << 16 \
+ | (%(PREFIX)s_EVENT_ ## event) << 8 \
+ | (%(PREFIX)s_STATE_ ## to))
+
+/* %(name)s branches. */
+enum %(prefix)s_branch_t
+{
+%(branches)s};
+typedef enum %(prefix)s_branch_t %(prefix)s_branch_t;
+
+#undef _BRANCH
+
+/* %(name)s transition type. */
+typedef %(prefix)s_branch_t (*%(prefix)s_transition_t) (%(user.type)s *user);
+
+/* Initialise %(name)s automaton. */
+void
+%(prefix)s_init (%(user.type)s *user);
+
+/* Handle events on %(name)s automaton. */
+void
+%(prefix)s_handle_event (%(user.type)s *user, %(prefix)s_event_t event);
+
+/* Value to return to follow the only branch. */
+#define %(prefix)s_next(state, event) \
+ %(PREFIX)s_BRANCH__ ## state ## __ ## event ## __
+
+/* Value to return to follow a given branch. */
+#define %(prefix)s_next_branch(state, event, branch) \
+ %(PREFIX)s_BRANCH__ ## state ## __ ## event ## __ ## branch
+
+#endif /* %(prefix)s_h */
diff --git a/tools/dfagen/dfagen/output/c/template_cb.h b/tools/dfagen/dfagen/output/c/template_cb.h
new file mode 100644
index 00000000..6252b28c
--- /dev/null
+++ b/tools/dfagen/dfagen/output/c/template_cb.h
@@ -0,0 +1,12 @@
+#ifndef %(prefix)s_cb_h
+#define %(prefix)s_cb_h
+/*
+ * THIS IS AN AUTOMATICALLY GENERATED FILE, DO NOT EDIT!
+ *
+ * %(name)s callbacks declaration.
+ *
+%(*comments)s */
+
+#include "%(prefix)s.h"
+
+%(cb_decl)s#endif /* %(prefix)s_cb_h */
diff --git a/tools/dfagen/dfagen/output/c/template_cb_decl.h b/tools/dfagen/dfagen/output/c/template_cb_decl.h
new file mode 100644
index 00000000..a1e7c0f2
--- /dev/null
+++ b/tools/dfagen/dfagen/output/c/template_cb_decl.h
@@ -0,0 +1,6 @@
+/*
+ * %(state)s =%(event)s=>
+%(*branches_to)s */
+%(prefix)s_branch_t
+%(prefix)s__%(state)s__%(event)s (%(user.type)s *user);
+
diff --git a/tools/dfagen/dfagen/output/c/template_cb_impl.c b/tools/dfagen/dfagen/output/c/template_cb_impl.c
new file mode 100644
index 00000000..ffd9720f
--- /dev/null
+++ b/tools/dfagen/dfagen/output/c/template_cb_impl.c
@@ -0,0 +1,9 @@
+/*
+ * %(state)s =%(event)s=>
+%(*branches_to)s */
+%(prefix)s_branch_t
+%(prefix)s__%(state)s__%(event)s (%(user.type)s *user)
+{
+%(returns)s
+}
+
diff --git a/tools/dfagen/dfagen/output/c/template_cb_skel.c b/tools/dfagen/dfagen/output/c/template_cb_skel.c
new file mode 100644
index 00000000..95abbd42
--- /dev/null
+++ b/tools/dfagen/dfagen/output/c/template_cb_skel.c
@@ -0,0 +1,9 @@
+/*
+ * THIS IS AN AUTOMATICALLY GENERATED FILE, DO NOT EDIT!
+ *
+ * Skeleton for %(name)s callbacks implementation.
+ *
+%(*comments)s */
+#include "%(prefix)s_cb.h"
+
+%(cb_impl)s
diff --git a/tools/dfagen/dfagen/output/dot.py b/tools/dfagen/dfagen/output/dot.py
new file mode 100644
index 00000000..100cbb8f
--- /dev/null
+++ b/tools/dfagen/dfagen/output/dot.py
@@ -0,0 +1,19 @@
+
+def write (prefix, automaton, user):
+ output = prefix + '.dot'
+ o = open (output, 'w')
+ o.write ('digraph %s {' % prefix)
+ for s in automaton.states.values ():
+ o.write (' %s\n' % s.name)
+ for tr in s.transitions.values ():
+ for br in tr.branches.values ():
+ o.write (' %(state)s -> %(to)s [ label = "%(event)s" ];\n'
+ % dict (
+ state = s.name,
+ event = tr.event.name
+ + (br.name and ': ' + br.name or ''),
+ to = (br.to and br.to.name or s.name),
+ )
+ )
+ o.write ('}')
+ o.close ()
diff --git a/tools/dfagen/dfagen/parser.g b/tools/dfagen/dfagen/parser.g
new file mode 100644
index 00000000..62f9f4d1
--- /dev/null
+++ b/tools/dfagen/dfagen/parser.g
@@ -0,0 +1,62 @@
+from dfagen.automaton import *
+
+%%
+
+parser AutomatonParser:
+ ignore: "(#.*?)?\n"
+ token EOF: "$"
+ token COMMENTS: " .+?\n"
+ token STATE: "\w+"
+ token EVENT: "\w([\w ]*\w)?"
+ token QUALIFIER: "\w([\w ]*\w)?"
+ token ATITLE: ".*?\n"
+
+ rule automaton: ATITLE {{ a = Automaton (ATITLE.strip ()) }}
+ ( comments {{ a.comments = comments }}
+ ) ?
+ "States:\n"
+ ( statedef {{ a.add_state (statedef) }}
+ ) *
+ "Events:\n"
+ ( eventdef {{ a.add_event (eventdef) }}
+ ) *
+ ( transdef<<a>>
+ ) *
+ EOF {{ return a }}
+
+ rule statedef: " " STATE {{ s = State (STATE) }}
+ "\n"
+ ( comments {{ s.comments = comments }}
+ ) ?
+ {{ return s }}
+
+ rule eventdef: " " EVENT {{ e = Event (EVENT) }}
+ "\n"
+ ( comments {{ e.comments = comments }}
+ ) ?
+ {{ return e }}
+
+ rule transdef<<a>>: transsl<<a>>
+ ( trans<<a>> {{ for s in transsl: s.add_branch (trans) }}
+ ) *
+
+ rule transsl<<a>>: {{ sl = [ ] }}
+ STATE {{ sl.append (a.states[STATE]) }}
+ ( ",\s*" STATE {{ sl.append (a.states[STATE]) }}
+ ) *
+ ":\n" {{ return sl }}
+
+ rule trans<<a>>: " " EVENT {{ t = TransitionBranch (a.events[EVENT]) }}
+ ( ":\s*" QUALIFIER {{ t.name = QUALIFIER }}
+ ) ?
+ "\s*->\s*"
+ ( STATE {{ t.to = a.states[STATE] }}
+ | "\\." )
+ ( comments {{ t.comments = comments }}
+ ) ?
+ {{ return t }}
+
+ rule comments: COMMENTS {{ c = COMMENTS.strip () }}
+ ( COMMENTS {{ c += '\n' + COMMENTS.strip () }}
+ ) * {{ return c }}
+
diff --git a/tools/dfagen/doc/dfagen.txt b/tools/dfagen/doc/dfagen.txt
new file mode 100644
index 00000000..7e2b9abd
--- /dev/null
+++ b/tools/dfagen/doc/dfagen.txt
@@ -0,0 +1,181 @@
+====================================================
+dfagen - The Deterministic Finite Automata Generator
+====================================================
+
+Theory of operations
+====================
+
+Some solutions are easier to describe using an automaton. An automaton is
+composed of a set of states and transitions triggered by events. The
+transition is a condition to switch from one state to another.
+
+You can find many automata information on the web. Automata are also known
+as Finite State Machines.
+
+The dfagen program will read an automaton description and according to the
+selected output, can generate a different representation of this automaton or
+a program to implement it.
+
+C program output
+----------------
+
+With this output, dfagen will generate a program where code is executed on
+each transition.
+
+Graphviz output
+---------------
+
+This output is suitable to be read by Graphviz. This can be used to generate
+a graphic representation of the automaton.
+
+Automaton description
+=====================
+
+Comments are entered with a dash (``#``) on the first character of the line.
+They are completely ignored by dfagen.
+
+The file is divided into sections. The first one is the automaton name and
+text description, the second one is the list of states, the third one is the
+list of events, and all the followings are states descriptions. Sections must
+start on the first character of the line and text descriptions are always
+preceded by two spaces.
+
+Let us examine an example.
+
+Here is the first section::
+
+ # Second FSM example.
+ Example 2
+ A barman robot.
+
+The first line is completely ignored, the second line is the automaton name,
+and the third one is its text description and start with two spaces.
+
+Here is the state list section::
+
+ States:
+ IDLE
+ waiting for a command
+ DROPPING_ICE
+ FILLING_GLASS
+
+States are listed, starting with one space at the start of the line. Each
+state can have an associated text description.
+
+Here is the event list section::
+
+ Events:
+ command
+ ice dropped
+ glass filled
+ replace bottle
+
+It use the same syntax as the states list.
+
+Here is a state section::
+
+ DROPPING_ICE:
+ ice dropped -> FILLING_GLASS
+ close the ice door
+ start filling
+
+The first line gives the described state name. It can include several states,
+separated with comma, if they share the same description.
+
+The second line says: when the ``ice dropped`` event occurs, switch to the
+``FILLING_GLASS`` state.
+
+The other lines are text descriptions, they can be used by the programmer as a
+guideline to know what to implement on this transition.
+
+Sometimes, an event can trigger a different state, according to information
+which may be contained in the received event. In dfagen, this is called
+branches. To describe branches, use a colon::
+
+ IDLE:
+ command: with ice -> DROPPING_ICE
+ open the ice door
+ command: without ice -> FILLING_GLASS
+ start filling
+
+In this example, when a ``command`` event occurs, the ice switch may influence
+the target state.
+
+Sometimes, an event is handled, but the automaton does not change state. You
+can use a dot instead of the state name to describe this. This is useful to
+describe several states which behave the same way::
+
+ IDLE:
+ replace bottle -> .
+ reset glass counter
+
+ DROPPING_ICE, FILLING_GLASS:
+ command -> .
+ ignored
+
+Invoking dfagen
+===============
+
+Run the following command::
+
+ python dfagen.py -o c -d my_automaton.fsm -c my_output.conf -p my_prefix
+
+The ``-o`` option chooses the output method, the ``-d`` option gives your
+automaton description, the ``-c`` option gives the output configuration file,
+and the ``-p`` is the prefix, which is used to name output files.
+
+..warning: This is subject to change.
+
+Outputs
+=======
+
+C program
+---------
+
+Output name: ``c``
+
+Here is an example output configuration file::
+
+ [user]
+ type = robot_t
+ type-forward-decl = typedef struct robot_t robot_t;
+ type-decl = #include "ex2_robot.h"
+ field = fsm
+
+The ``type`` item is the C type containing the automaton and which must be
+passed to automaton functions. The ``field`` item is the name of the
+automaton member inside the previous type. For example::
+
+ struct robot_t
+ {
+ ex2_state_t fsm;
+ int bottle;
+ };
+
+The ``type-decl`` item is the C code used to declare the type, and the
+``type-forward-decl`` item is used in the generated C header to forward
+declare the type.
+
+When dfagen is run, it generates four different files:
+
+- ``prefix.h``, the header to declare the automaton and its associated symbols
+ and functions,
+- ``prefix.c``, the source file containing the transitions table and logic,
+- ``prefix_cb.h``, the header to declare the transition callbacks,
+- ``prefix_cb_skel.c``, the source file to be customised by the user.
+
+The first time you run dfagen, copy ``prefix_cb_skel.c`` to ``prefix_cb.c``
+and edit the copy. Next time, you will merge new elements to your version
+(using a merge program like vimdiff or meld is a good idea).
+
+The ``prefix.c`` file contains a function to be called each time en event
+occurs. This function will run the corresponding transition callback and will
+check its return value.
+
+Graphviz output
+---------------
+
+Output name: ``dot``
+
+There is currently no output configuration file. Run ``dot`` (from the
+Graphviz distribution to get a graphic output of the automaton.
diff --git a/tools/dfagen/examples/Makefile b/tools/dfagen/examples/Makefile
new file mode 100644
index 00000000..b78f1121
--- /dev/null
+++ b/tools/dfagen/examples/Makefile
@@ -0,0 +1,40 @@
+CFLAGS = -O2 -Wall
+
+all: ex1 ex2 ex1.png ex2.png
+
+ex1: ex1.o ex1_cb.o
+
+ex1.c: ex1.fsm ex1.conf
+ python ../dfagen.py -o c -d ex1.fsm -c ex1.conf -p ex1
+
+ex1.h ex1_cb_skel.c ex1_cb.h: ex1.c
+
+ex1_cb.c: ex1_cb_skel.c ex1_cb.c.patch
+ cp $< $@
+ patch $@ ex1_cb.c.patch
+
+ex1.o: ex1_cb.h ex1.h
+ex1_cb.o: ex1_cb.h ex1.h
+
+ex2: ex2.o ex2_cb.o ex2_robot.o
+
+ex2.c: ex2.fsm ex2.conf
+ python ../dfagen.py -o c -d ex2.fsm -c ex2.conf -p ex2
+
+ex2.h ex2_cb_skel.c ex2_cb.h: ex2.c
+
+ex2_cb.c: ex2_cb_skel.c ex2_cb.c.patch
+ cp $< $@
+ patch $@ ex2_cb.c.patch
+
+%.dot: %.fsm
+ python ../dfagen.py -o dot -d $< -p $(@:%.dot=%)
+
+%.png: %.dot
+ dot -Tpng $< -o $@
+
+clean:
+ rm -f ex1 ex1.o ex1_cb.o ex1.c ex1.h ex1_cb_skel.c ex1_cb.h ex1_cb.c
+ rm -f ex1.dot ex1.png
+ rm -f ex2 ex2.o ex2_cb.o ex2_robot.o ex2.c ex2.h ex2_cb_skel.c ex2_cb.h ex2_cb.c
+ rm -f ex2.dot ex2.png
diff --git a/tools/dfagen/examples/ex1.conf b/tools/dfagen/examples/ex1.conf
new file mode 100644
index 00000000..40a7f419
--- /dev/null
+++ b/tools/dfagen/examples/ex1.conf
@@ -0,0 +1,5 @@
+[user]
+type = door_t
+type-forward-decl = typedef struct door_t door_t;
+type-decl = struct door_t { ex1_state_t fsm; };
+field = fsm
diff --git a/tools/dfagen/examples/ex1.fsm b/tools/dfagen/examples/ex1.fsm
new file mode 100644
index 00000000..3a8f083b
--- /dev/null
+++ b/tools/dfagen/examples/ex1.fsm
@@ -0,0 +1,21 @@
+# First FSM example.
+Example 1
+ A door which can be open or closed.
+
+States:
+ OPEN
+ The door is open
+ CLOSED
+ The door is clossed
+
+Events:
+ open
+ close
+
+OPEN:
+ close -> CLOSED
+ Close the door
+
+CLOSED:
+ open -> OPEN
+ Open the door
diff --git a/tools/dfagen/examples/ex1_cb.c.patch b/tools/dfagen/examples/ex1_cb.c.patch
new file mode 100644
index 00000000..bc4cc924
--- /dev/null
+++ b/tools/dfagen/examples/ex1_cb.c.patch
@@ -0,0 +1,41 @@
+--- ex1_cb_skel.c 2008-01-06 18:00:55.000000000 +0100
++++ ex1_cb.c 2008-01-06 18:02:10.000000000 +0100
+@@ -7,6 +7,10 @@
+ */
+ #include "ex1_cb.h"
+
++#include <stdio.h>
++
++struct door_t { ex1_state_t fsm; };
++
+ /*
+ * OPEN =close=>
+ * => CLOSED
+@@ -15,6 +19,7 @@
+ ex1_branch_t
+ ex1__OPEN__close (door_t *user)
+ {
++ printf ("close the door\n");
+ return ex1_next (OPEN, close);
+ }
+
+@@ -26,7 +31,18 @@
+ ex1_branch_t
+ ex1__CLOSED__open (door_t *user)
+ {
++ printf ("open the door\n");
+ return ex1_next (CLOSED, open);
+ }
+
+-
++int
++main (void)
++{
++ door_t door;
++ ex1_init (&door);
++ ex1_handle_event (&door, EX1_EVENT_close);
++ ex1_handle_event (&door, EX1_EVENT_open);
++ printf ("now, will crash:\n");
++ ex1_handle_event (&door, EX1_EVENT_open);
++ return 0;
++}
diff --git a/tools/dfagen/examples/ex2.conf b/tools/dfagen/examples/ex2.conf
new file mode 100644
index 00000000..0e20b765
--- /dev/null
+++ b/tools/dfagen/examples/ex2.conf
@@ -0,0 +1,5 @@
+[user]
+type = robot_t
+type-forward-decl = typedef struct robot_t robot_t;
+type-decl = #include "ex2_robot.h"
+field = fsm
diff --git a/tools/dfagen/examples/ex2.fsm b/tools/dfagen/examples/ex2.fsm
new file mode 100644
index 00000000..e290c05c
--- /dev/null
+++ b/tools/dfagen/examples/ex2.fsm
@@ -0,0 +1,40 @@
+# Second FSM example.
+Example 2
+ A barman robot.
+
+States:
+ IDLE
+ waiting for a command
+ DROPPING_ICE
+ FILLING_GLASS
+
+Events:
+ command
+ ice dropped
+ glass filled
+ replace bottle
+
+IDLE:
+ command: with ice -> DROPPING_ICE
+ open the ice door
+ command: without ice -> FILLING_GLASS
+ start filling
+ command: empty bottle -> .
+ display "empty bottle, please replace it"
+ replace bottle -> .
+ reset glass counter
+
+DROPPING_ICE:
+ ice dropped -> FILLING_GLASS
+ close the ice door
+ start filling
+
+FILLING_GLASS:
+ glass filled -> IDLE
+ stop filling
+
+DROPPING_ICE, FILLING_GLASS:
+ command -> .
+ ignored
+ replace bottle -> .
+ ignored
diff --git a/tools/dfagen/examples/ex2_cb.c.patch b/tools/dfagen/examples/ex2_cb.c.patch
new file mode 100644
index 00000000..f1402431
--- /dev/null
+++ b/tools/dfagen/examples/ex2_cb.c.patch
@@ -0,0 +1,67 @@
+--- ex2_cb_skel.c 2008-01-06 18:02:50.000000000 +0100
++++ ex2_cb.c 2008-01-06 18:02:50.000000000 +0100
+@@ -6,6 +6,9 @@
+ * A barman robot.
+ */
+ #include "ex2_cb.h"
++#include "ex2_robot.h"
++
++#include <stdio.h>
+
+ /*
+ * FILLING_GLASS =command=>
+@@ -37,6 +40,7 @@
+ ex2_branch_t
+ ex2__FILLING_GLASS__glass_filled (robot_t *user)
+ {
++ puts ("stop filling");
+ return ex2_next (FILLING_GLASS, glass_filled);
+ }
+
+@@ -48,6 +52,8 @@
+ ex2_branch_t
+ ex2__IDLE__replace_bottle (robot_t *user)
+ {
++ puts ("reset glass counter");
++ user->bottle = 3;
+ return ex2_next (IDLE, replace_bottle);
+ }
+
+@@ -63,9 +69,25 @@
+ ex2_branch_t
+ ex2__IDLE__command (robot_t *user)
+ {
+- return ex2_next_branch (IDLE, command, empty_bottle);
+- return ex2_next_branch (IDLE, command, without_ice);
+- return ex2_next_branch (IDLE, command, with_ice);
++ if (user->bottle)
++ {
++ user->bottle--;
++ if (user->ice)
++ {
++ puts ("open the ice door");
++ return ex2_next_branch (IDLE, command, with_ice);
++ }
++ else
++ {
++ puts ("start filling");
++ return ex2_next_branch (IDLE, command, without_ice);
++ }
++ }
++ else
++ {
++ puts ("empty bottle, please replace it");
++ return ex2_next_branch (IDLE, command, empty_bottle);
++ }
+ }
+
+ /*
+@@ -99,6 +121,8 @@
+ ex2_branch_t
+ ex2__DROPPING_ICE__ice_dropped (robot_t *user)
+ {
++ puts ("close the ice door");
++ puts ("start filling");
+ return ex2_next (DROPPING_ICE, ice_dropped);
+ }
+
diff --git a/tools/dfagen/examples/ex2_robot.c b/tools/dfagen/examples/ex2_robot.c
new file mode 100644
index 00000000..93281f1a
--- /dev/null
+++ b/tools/dfagen/examples/ex2_robot.c
@@ -0,0 +1,39 @@
+/* Example 2 extra code. */
+#include "ex2_robot.h"
+
+#include <stdio.h>
+
+int
+main (void)
+{
+ robot_t robot;
+ ex2_init (&robot);
+ robot.bottle = 3;
+ puts ("A glass:");
+ robot.ice = 1;
+ ex2_handle_event (&robot, EX2_EVENT_command);
+ ex2_handle_event (&robot, EX2_EVENT_ice_dropped);
+ ex2_handle_event (&robot, EX2_EVENT_glass_filled);
+ puts ("Another glass:");
+ robot.ice = 0;
+ ex2_handle_event (&robot, EX2_EVENT_command);
+ ex2_handle_event (&robot, EX2_EVENT_glass_filled);
+ puts ("Yet another glass:");
+ robot.ice = 0;
+ ex2_handle_event (&robot, EX2_EVENT_command);
+ ex2_handle_event (&robot, EX2_EVENT_glass_filled);
+ puts ("There is no more liquid:");
+ robot.ice = 0;
+ ex2_handle_event (&robot, EX2_EVENT_command);
+ puts ("Replace bootle:");
+ ex2_handle_event (&robot, EX2_EVENT_replace_bottle);
+ puts ("Another glass:");
+ robot.ice = 1;
+ ex2_handle_event (&robot, EX2_EVENT_command);
+ puts ("Commands are ignore while the robot is fonctionning:");
+ ex2_handle_event (&robot, EX2_EVENT_command);
+ ex2_handle_event (&robot, EX2_EVENT_ice_dropped);
+ ex2_handle_event (&robot, EX2_EVENT_command);
+ ex2_handle_event (&robot, EX2_EVENT_glass_filled);
+ return 0;
+}
diff --git a/tools/dfagen/examples/ex2_robot.h b/tools/dfagen/examples/ex2_robot.h
new file mode 100644
index 00000000..0ceb7949
--- /dev/null
+++ b/tools/dfagen/examples/ex2_robot.h
@@ -0,0 +1,18 @@
+#ifndef ex2_robot_h
+#define ex2_robot_h
+/* Example 2 type definition. */
+
+#include "ex2.h"
+
+/* Robot structure. */
+struct robot_t
+{
+ /* The generated FSM. */
+ ex2_state_t fsm;
+ /* True for command with ice. */
+ int ice;
+ /* Number of glasses in the bottle. */
+ int bottle;
+};
+
+#endif /* ex2_robot_h */