Skip to content

Commit

Permalink
fsm_print_fsm: add option for printing grouped edges, add CLI flag.
Browse files Browse the repository at this point in the history
Add a flag to `struct fsm_options`, `group_edges`, which tells
fsm to print edges in a group whenever the output method supports
it.

This can make fsm output much easier to read with FSMs from unanchored
regexes, which often have hundreds of "...and everything else 0x00 -
0xFF goes back to state X" edges.

Without the `-g` flag, this output would be 1798 lines long:

    0; 1; 2; 3; 4; 5; 6; 7;

    0  ->  0 "\x00" .. "a", "c" .. "\xff";
    0  ->  1 "b";
    1  ->  0 "\x00" .. "`", "c" .. "\xff";
    1  ->  1 "b";
    1  ->  2 "a";
    2  ->  0 "\x00" .. "a", "c" .. "m", "o" .. "\xff";
    2  ->  1 "b";
    2  ->  3 "n";
    3  ->  0 "\x00" .. "`", "c" .. "\xff";
    3  ->  1 "b";
    3  ->  4 "a";
    4  ->  0 "\x00" .. "a", "c" .. "m", "o" .. "\xff";
    4  ->  1 "b";
    4  ->  5 "n";
    5  ->  0 "\x00" .. "`", "c" .. "\xff";
    5  ->  1 "b";
    5  ->  6 "a";
    6  ->  0 "\x00" .. "a", "c" .. "r", "t" .. "\xff";
    6  ->  1 "b";
    6  ->  7 "s";
    7  ->  7 ?;

    start: 0;
    end:   7;

Note that this change does not update the man page, any other
output modes (it would be nice to support this in `dot` later),
or make fsm able to read that input back in yet.
  • Loading branch information
silentbicycle authored and katef committed Apr 27, 2021
1 parent bad9947 commit 4ff6cba
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 58 deletions.
5 changes: 5 additions & 0 deletions include/fsm/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ struct fsm_options {
*/
unsigned int always_hex:1;

/* boolean: true indicates to group edges with a common destination in output,
* when possible, rather than printing them all individually.
*/
unsigned int group_edges:1;

/* for generated code, what kind of I/O API to generate */
enum fsm_io io;

Expand Down
5 changes: 3 additions & 2 deletions src/fsm/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ static void
usage(void)
{
printf("usage: fsm [-x] {<text> ...}\n");
printf(" fsm {-p} [-l <language>] [-aCcwX] [-k <io>] [-e <prefix>]\n");
printf(" fsm {-p} [-l <language>] [-aCcgwX] [-k <io>] [-e <prefix>]\n");
printf(" fsm {-dmr | -t <transformation>} [-i <iterations>] [<file.fsm> | <file-a> <file-b>]\n");
printf(" fsm {-q <query>} [<file>]\n");
printf(" fsm {-W <maxlen>} <file.fsm>\n");
Expand Down Expand Up @@ -394,11 +394,12 @@ main(int argc, char *argv[])
{
int c;

while (c = getopt(argc, argv, "h" "aCcwXe:k:i:" "xpq:l:dGmrt:W:"), c != -1) {
while (c = getopt(argc, argv, "h" "aCcgwXe:k:i:" "xpq:l:dGmrt:W:"), c != -1) {
switch (c) {
case 'a': opt.anonymous_states = 1; break;
case 'c': opt.consolidate_edges = 1; break;
case 'C': opt.comments = 0; break;
case 'g': opt.group_edges = 1; break;
case 'w': opt.fragment = 1; break;
case 'X': opt.always_hex = 1; break;
case 'e': opt.prefix = optarg; break;
Expand Down
199 changes: 143 additions & 56 deletions src/libfsm/print/fsm.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@
#include <fsm/print.h>
#include <fsm/options.h>

static int
fprintf_state(FILE *f, const struct fsm *fsm, fsm_state_t s);

static int
fprintf_state_comments(FILE *f, const struct fsm *fsm, fsm_state_t dst);

/* TODO: centralise */
static int
findany(const struct fsm *fsm, fsm_state_t state, fsm_state_t *a)
Expand Down Expand Up @@ -110,29 +116,123 @@ fsm_print_fsm(FILE *f, const struct fsm *fsm)
}

for (s = 0; s < fsm->statecount; s++) {
struct fsm_edge e;
struct edge_ordered_iter eoi;
if (!fprintf_state(f, fsm, s)) {
return;
}
}

{
struct state_iter jt;
fsm_state_t st;
fprintf(f, "\n");

for (state_set_reset(fsm->states[s].epsilons, &jt); state_set_next(&jt, &st); ) {
fprintf(f, "%-2u -> %2u;\n", s, st);
}
if (!fsm_getstart(fsm, &start)) {
return;
}

fprintf(f, "start: %u;\n", start);

end = fsm->endcount;

if (end == 0) {
return;
}

fprintf(f, "end: ");
for (s = 0; s < fsm->statecount; s++) {
if (fsm_isend(fsm, s)) {
end--;

fprintf(f, "%u%s", s, end > 0 ? ", " : ";\n");
}
}
}

{
fsm_state_t a;
static void
fprintf_char_range(FILE *f, const struct fsm_options *opt, char lower, char upper)
{
if (lower == upper) {
fputs("\"", f);
fsm_escputc(f, opt, (char)lower);
fputs("\"", f);
} else {
fputs("\"", f);
fsm_escputc(f, opt, (char)lower);
fputs("\" .. \"", f);
fsm_escputc(f, opt, (char)upper);
fputs("\"", f);
}
}

if (findany(fsm, s, &a)) {
fprintf(f, "%-2u -> %2u ?;\n", s, a);
continue;
}
static int
fprintf_state(FILE *f, const struct fsm *fsm, fsm_state_t s)
{
{
struct state_iter jt;
fsm_state_t st;

for (state_set_reset(fsm->states[s].epsilons, &jt); state_set_next(&jt, &st); ) {
fprintf(f, "%-2u -> %2u;\n", s, st);
}
}

{
fsm_state_t a;

if (findany(fsm, s, &a)) {
fprintf(f, "%-2u -> %2u ?;\n", s, a);
return 1;
}
}

assert(s < fsm->statecount);
assert(s < fsm->statecount);
if (fsm->opt->group_edges) {
struct edge_group_iter egi;
struct edge_group_iter_info info;

edge_set_group_iter_reset(fsm->states[s].edges,
EDGE_GROUP_ITER_ALL, &egi);
while (edge_set_group_iter_next(&egi, &info)) {
unsigned i, ranges = 0;
unsigned lower = 256; /* start with lower bound out of range */
assert(info.to < fsm->statecount);

fprintf(f, "%-2u -> %2u ", s, info.to);

for (i = 0; i < 256; i++) {
if (info.symbols[i/64] & (1UL << (i & 63))) {
if (lower > i) {
lower = i; /* set lower bound for range */
}
} else {
if (lower < i) {
if (ranges > 0) {
fputs(", ", f);
}

fprintf_char_range(f, fsm->opt, lower, (char)i - 1);
ranges++;
}
lower = 256;
}
}
if (lower < 256) { /* close last range */
if (ranges > 0) {
fputs(", ", f);
}
fprintf_char_range(f, fsm->opt, lower, (char)255);
}

fprintf(f, ";");

if (fsm->opt->comments) {
if (!fprintf_state_comments(f, fsm, info.to)) {
return 0;
}
}

fprintf(f, "\n");
}
} else {
struct fsm_edge e;
struct edge_ordered_iter eoi;
for (edge_set_ordered_iter_reset(fsm->states[s].edges, &eoi);
edge_set_ordered_iter_next(&eoi, &e); ) {
assert(e.state < fsm->statecount);
Expand All @@ -146,56 +246,43 @@ fsm_print_fsm(FILE *f, const struct fsm *fsm)
fprintf(f, ";");

if (fsm->opt->comments) {
fsm_state_t start;

if (fsm_getstart(fsm, &start)) {
if (e.state == start) {
fprintf(f, " # start");
} else if (!fsm_has(fsm, fsm_hasepsilons)) {
char buf[50];
int n;

n = fsm_example(fsm, e.state, buf, sizeof buf);
if (-1 == n) {
perror("fsm_example");
return;
}

if (n > 0) {
fprintf(f, " # e.g. \"");
escputs(f, fsm->opt, fsm_escputc, buf);
fprintf(f, "%s\"",
n >= (int) sizeof buf - 1 ? "..." : "");
}
}
if (!fprintf_state_comments(f, fsm, e.state)) {
return 0;
}
}

fprintf(f, "\n");
}
}

fprintf(f, "\n");

if (!fsm_getstart(fsm, &start)) {
return;
}

fprintf(f, "start: %u;\n", start);

end = fsm->endcount;

if (end == 0) {
return;
}
return 1;
}

fprintf(f, "end: ");
for (s = 0; s < fsm->statecount; s++) {
if (fsm_isend(fsm, s)) {
end--;
static int
fprintf_state_comments(FILE *f, const struct fsm *fsm, fsm_state_t dst)
{
fsm_state_t start;

if (fsm_getstart(fsm, &start)) {
if (dst == start) {
fprintf(f, " # start");
} else if (!fsm_has(fsm, fsm_hasepsilons)) {
char buf[50];
int n;

n = fsm_example(fsm, dst, buf, sizeof buf);
if (-1 == n) {
perror("fsm_example");
return 0;
}

fprintf(f, "%u%s", s, end > 0 ? ", " : ";\n");
if (n > 0) {
fprintf(f, " # e.g. \"");
escputs(f, fsm->opt, fsm_escputc, buf);
fprintf(f, "%s\"",
n >= (int) sizeof buf - 1 ? "..." : "");
}
}
}
return 1;
}

0 comments on commit 4ff6cba

Please sign in to comment.