#!/usr/bin/env perl # # sm2dot # # Converts a state machine description file to a file readable by dot. dot is # a program which creates printable directed graphs from a directed graph # description. # use strict; my ($filename); if ($#ARGV >= 0) { $filename = $ARGV[0]; } else { $filename = "&STDIN"; } if (!open(FILE, "<$filename")) { print STDERR "\nERROR: unable to open $filename\n\n"; exit(1); } my (@sm, $sm); @sm = ; $sm = "\n" . join("", @sm) . "\n"; my($re_keyword, $re_str, $re_params, $re); $re_keyword = "Graph|State|Event|State_Change|Change_State|" . "Action|Invoke_Action"; $re_str = "\\s*\\[[^\]]*\\]\\s*"; $re_params = "((${re_str})?,)*(($re_str))"; $re = "(^|\\n)\\s*($re_keyword)\\s*\\(?$re_params\\)?"; my($keyword, @params_tmp, $graph_name, $graph_params, %nodes, @node_names, @edges, $cur_node, $tmp); $graph_name = "My Graph"; $graph_params = ""; while (($tmp, $keyword, @params_tmp) = ($sm =~ /$re/)) { my ($i, @params); for ($i = 1; $i <= $#params_tmp; $i += 2) { if (defined($params_tmp[$i])) { $params_tmp[$i] =~ s/^\s*\[\s*//; $params_tmp[$i] =~ s/\s*\]\s*//; $params_tmp[$i] =~ s/\s+/ /g; $params[$#params+1] = $params_tmp[$i]; } } if ($keyword eq "State_Change" || $keyword eq "Change_State" || $keyword eq "Invoke_Action") { my($attrs); $edges[$#edges+1] = join("\0", $cur_node, $params[0], defined($params[1]) ? $params[1] : ""); } elsif ($keyword eq "Event") { # this isn't being used at the moment... # $cur_event = $params[0]; } elsif ($keyword eq "State" || $keyword eq "Action") { $cur_node = $params[0]; my($opt_attrs, @attrs); $opt_attrs = defined($params[1]) ? $params[1] : ""; if (!defined($nodes{$cur_node})) { # print STDERR "OPT_ATTRS = \"$opt_attrs\"\n"; if (!($opt_attrs =~ /shape\s*=/)) { $attrs[$#attrs+1] = ($keyword eq "State") ? "shape=ellipse" : "shape=box"; # print STDERR "SETTING SHAPE = \"$attrs[$#attrs]\"\n"; } if ($opt_attrs ne "") { $attrs[$#attrs+1] = $opt_attrs; } $nodes{$cur_node} = join(",", @attrs); $node_names[$#node_names+1] = $cur_node; } else { print STDERR "Warning state $cur_node defined multiple times\n"; } } elsif ($keyword eq "Graph") { $graph_name = $params[0] if defined($params[0]); $graph_params = $params[1] if defined($params[1]); } # print STDERR "DECL - $keyword", join("|","", @params, ""),"\n"; $sm = $'; } close(FILE); my($edge, $from_node, $to_node, $error); $error = 0; foreach $edge (@edges) { ($from_node, $to_node) = split("\0", $edge); if (!defined($nodes{$from_node})) { print STDERR "Unable to find from node \"$from_node\"\n"; $error = 1; } if (!defined($nodes{$to_node})) { print STDERR "Unable to find to node \"$to_node\"\n"; $error = 1; } } exit(1) if ($error != 0); print "digraph \"$graph_name\" {\n"; print "graph [$graph_params];\n" if $graph_params ne ""; my($node,$attrs); foreach $node (@node_names) { print "\"$node\""; print (($nodes{$node} ne "") ? " [$nodes{$node}];\n" : ";\n"); } my($edge, $from_node, $to_node, $attrs); foreach $edge (@edges) { ($from_node, $to_node, $attrs) = split("\0", $edge); print "\"$from_node\" -> \"$to_node\""; print (($attrs ne "") ? " [$attrs];\n" : ";\n"); } print "}\n"; exit(0);