From 164ac3a34cbac441e82b256c97cb8784ea9d482c Mon Sep 17 00:00:00 2001 From: Nicolas Schodet Date: Mon, 17 Mar 2008 22:53:01 +0100 Subject: * tools/dfagen: - added dfagen. --- tools/dfagen/dfagen.py | 3 + tools/dfagen/dfagen/__init__.py | 0 tools/dfagen/dfagen/automaton.py | 111 +++++++++++++++ tools/dfagen/dfagen/command.py | 23 +++ tools/dfagen/dfagen/output/__init__.py | 24 ++++ tools/dfagen/dfagen/output/c.py | 154 ++++++++++++++++++++ tools/dfagen/dfagen/output/c/template.c | 39 +++++ tools/dfagen/dfagen/output/c/template.h | 59 ++++++++ tools/dfagen/dfagen/output/c/template_cb.h | 12 ++ tools/dfagen/dfagen/output/c/template_cb_decl.h | 6 + tools/dfagen/dfagen/output/c/template_cb_impl.c | 9 ++ tools/dfagen/dfagen/output/c/template_cb_skel.c | 9 ++ tools/dfagen/dfagen/output/dot.py | 19 +++ tools/dfagen/dfagen/parser.g | 62 ++++++++ tools/dfagen/doc/dfagen.txt | 181 ++++++++++++++++++++++++ tools/dfagen/examples/Makefile | 40 ++++++ tools/dfagen/examples/ex1.conf | 5 + tools/dfagen/examples/ex1.fsm | 21 +++ tools/dfagen/examples/ex1_cb.c.patch | 41 ++++++ tools/dfagen/examples/ex2.conf | 5 + tools/dfagen/examples/ex2.fsm | 40 ++++++ tools/dfagen/examples/ex2_cb.c.patch | 67 +++++++++ tools/dfagen/examples/ex2_robot.c | 39 +++++ tools/dfagen/examples/ex2_robot.h | 18 +++ 24 files changed, 987 insertions(+) create mode 100644 tools/dfagen/dfagen.py create mode 100644 tools/dfagen/dfagen/__init__.py create mode 100644 tools/dfagen/dfagen/automaton.py create mode 100644 tools/dfagen/dfagen/command.py create mode 100644 tools/dfagen/dfagen/output/__init__.py create mode 100644 tools/dfagen/dfagen/output/c.py create mode 100644 tools/dfagen/dfagen/output/c/template.c create mode 100644 tools/dfagen/dfagen/output/c/template.h create mode 100644 tools/dfagen/dfagen/output/c/template_cb.h create mode 100644 tools/dfagen/dfagen/output/c/template_cb_decl.h create mode 100644 tools/dfagen/dfagen/output/c/template_cb_impl.c create mode 100644 tools/dfagen/dfagen/output/c/template_cb_skel.c create mode 100644 tools/dfagen/dfagen/output/dot.py create mode 100644 tools/dfagen/dfagen/parser.g create mode 100644 tools/dfagen/doc/dfagen.txt create mode 100644 tools/dfagen/examples/Makefile create mode 100644 tools/dfagen/examples/ex1.conf create mode 100644 tools/dfagen/examples/ex1.fsm create mode 100644 tools/dfagen/examples/ex1_cb.c.patch create mode 100644 tools/dfagen/examples/ex2.conf create mode 100644 tools/dfagen/examples/ex2.fsm create mode 100644 tools/dfagen/examples/ex2_cb.c.patch create mode 100644 tools/dfagen/examples/ex2_robot.c create mode 100644 tools/dfagen/examples/ex2_robot.h (limited to 'tools') 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 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 +#include + +%(_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<> + ) * + 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<>: transsl<> + ( trans<> {{ for s in transsl: s.add_branch (trans) }} + ) * + + rule transsl<>: {{ sl = [ ] }} + STATE {{ sl.append (a.states[STATE]) }} + ( ",\s*" STATE {{ sl.append (a.states[STATE]) }} + ) * + ":\n" {{ return sl }} + + rule trans<>: " " 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 ++ ++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 + + /* + * 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 + +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 */ -- cgit v1.2.3