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/__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 ++++++++++ 13 files changed, 527 insertions(+) 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 (limited to 'tools/dfagen/dfagen') 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 }} + -- cgit v1.2.3