summaryrefslogtreecommitdiff
path: root/tools/dfagen/dfagen
diff options
context:
space:
mode:
Diffstat (limited to 'tools/dfagen/dfagen')
-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
13 files changed, 527 insertions, 0 deletions
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 }}
+