Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A different kind of json #198

Merged
merged 3 commits into from
Jan 19, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
278 changes: 219 additions & 59 deletions src/libfsm/print/json.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,113 +5,273 @@
*/

#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

#include "libfsm/internal.h" /* XXX: up here for bitmap.h */

#include <print/esc.h>

#include <adt/set.h>
#include <adt/bitmap.h>
#include <adt/stateset.h>
#include <adt/edgeset.h>

#include <fsm/fsm.h>
#include <fsm/pred.h>
#include <fsm/print.h>
#include <fsm/options.h>

#include <adt/set.h>
#include <adt/stateset.h>
#include <adt/edgeset.h>
static void
print_edge_epsilon(FILE *f, int *notfirst,
fsm_state_t src, fsm_state_t dst)
{
assert(f != NULL);
assert(notfirst != NULL);

#include "libfsm/internal.h"
if (*notfirst) {
fprintf(f, ",\n");
}

void
fsm_print_json(FILE *f, const struct fsm *fsm)
fprintf(f, " { \"src\": %u, \"dst\": %u }", src, dst);

*notfirst = 1;
}

static void
print_edge_symbol(FILE *f, int *notfirst, const struct fsm_options *opt,
fsm_state_t src, fsm_state_t dst, unsigned char symbol)
{
fsm_state_t i;
assert(f != NULL);
assert(notfirst != NULL);

if (*notfirst) {
fprintf(f, ",\n");
}

/* "symbol" as opposed to "label" because this is a literal value */
fprintf(f, " { \"src\": %u, \"dst\": %u, \"symbol\": ",
src, dst);

if (opt->always_hex) {
/* json doesn't have hex numeric literals, but this idea here is
* to output a numeric value rather than a string, so %u will do. */
fprintf(f, "%u", symbol);
} else {
fprintf(f, "\"");
json_escputc(f, opt, symbol);
fprintf(f, "\"");
}

fprintf(f, " }");

*notfirst = 1;
}

static void
print_edge_bitmap(FILE *f, int *notfirst, const struct fsm_options *opt,
fsm_state_t src, fsm_state_t dst, const struct bm *bm)
{
assert(f != NULL);
assert(fsm != NULL);
assert(notfirst != NULL);
assert(opt != NULL);
assert(bm != NULL);

fprintf(f, "{\n");
if (*notfirst) {
fprintf(f, ",\n");
}

{
fprintf(f, "\t\"states\": [\n");
fprintf(f, " { \"src\": %u, \"dst\": %u, ", src, dst);

for (i = 0; i < fsm->statecount; i++) {
struct fsm_edge e;
struct edge_iter it;
int first = 1;
/* "label" as opposed to "symbol" because this is a human-readable string */
fprintf(f, "\"label\": \"");

fprintf(f, "\t\t{\n");
(void) bm_print(f, opt, bm, 0, json_escputc);

fprintf(f, "\t\t\t\"end\": %s,\n",
fsm_isend(fsm, i) ? "true" : "false");
fprintf(f, "\" }");

fprintf(f, "\t\t\t\"edges\": [\n");
*notfirst = 1;
}

{
struct state_iter jt;
fsm_state_t st;
static void
print_edge_label(FILE *f, int *notfirst,
fsm_state_t src, fsm_state_t dst, const char *label)
{
assert(f != NULL);
assert(notfirst != NULL);
assert(label != NULL);

for (state_set_reset(fsm->states[i].epsilons, &jt); state_set_next(&jt, &st); ) {
if (!first) {
fprintf(f, ",\n");
}
if (*notfirst) {
fprintf(f, ",\n");
}

fprintf(f, "\t\t\t\t{ ");
fprintf(f, " { \"src\": %u, \"dst\": %u, \"label\": \"%s\" }",
src, dst, label);

fprintf(f, "\"char\": ");
fputs(" false", f);
fprintf(f, ", ");
*notfirst = 1;
}

fprintf(f, "\"to\": %u", st);
static void
singlestate(FILE *f, const struct fsm *fsm, fsm_state_t s, int *notfirst)
{
struct fsm_edge e;
struct edge_iter it;
struct state_set *unique;

/* XXX: should count .sl inside an edge */
fprintf(f, " }");
assert(f != NULL);
assert(fsm != NULL);
assert(fsm->opt != NULL);
assert(s < fsm->statecount);
assert(notfirst != NULL);

first = 0;
}
}
if (!fsm->opt->consolidate_edges) {
struct state_iter jt;
fsm_state_t st;

for (edge_set_reset(fsm->states[i].edges, &it); edge_set_next(&it, &e); ) {
if (!first) {
fprintf(f, ",\n");
}
for (state_set_reset(fsm->states[s].epsilons, &jt); state_set_next(&jt, &st); ) {
print_edge_epsilon(f, notfirst, s, st);
}

fprintf(f, "\t\t\t\t{ ");
for (edge_set_reset(fsm->states[s].edges, &it); edge_set_next(&it, &e); ) {
print_edge_symbol(f, notfirst, fsm->opt,
s, e.state, e.symbol);
}

fprintf(f, "\"char\": ");
fputs(" \"", f);
json_escputc(f, fsm->opt, e.symbol);
putc('\"', f);
return;
}

fprintf(f, ", ");
unique = NULL;

fprintf(f, "\"to\": %u", e.state);
for (edge_set_reset(fsm->states[s].edges, &it); edge_set_next(&it, &e); ) {
if (!state_set_add(&unique, fsm->opt->alloc, e.state)) {
/* TODO: error */
return;
}
}

fprintf(f, " }");
/*
* The consolidate_edges option is an aesthetic optimisation.
* For a state which has multiple edges all transitioning to the same state,
* all these edges are combined into a single edge, labelled with a more
* concise form of all their values.
*
* To implement this, we loop through all unique states, rather than
* looping through each edge.
*/
for (edge_set_reset(fsm->states[s].edges, &it); edge_set_next(&it, &e); ) {
struct fsm_edge ne;
struct edge_iter kt;
struct bm bm;

/* unique states only */
if (!state_set_contains(unique, e.state)) {
continue;
}

first = 0;
}
state_set_remove(&unique, e.state);

if (!first) {
fprintf(f, "\n");
bm_clear(&bm);

/* find all edges which go from this state to the same target state */
for (edge_set_reset(fsm->states[s].edges, &kt); edge_set_next(&kt, &ne); ) {
if (ne.state == e.state) {
bm_set(&bm, ne.symbol);
}
}

print_edge_bitmap(f, notfirst, fsm->opt, s, e.state, &bm);
}

fprintf(f, "\t\t\t]\n");
/*
* Special edges are not consolidated above
*/
{
struct state_iter jt;
fsm_state_t st;

fprintf(f, "\t\t}%s\n", (i + 1) < fsm->statecount ? "," : "");
for (state_set_reset(fsm->states[s].epsilons, &jt); state_set_next(&jt, &st); ) {
print_edge_label(f, notfirst, s, st, "\\u03B5");
}
}
}

void
fsm_print_json(FILE *f, const struct fsm *fsm)
{
fsm_state_t start;
fsm_state_t i;

assert(f != NULL);
assert(fsm != NULL);
assert(fsm->opt != NULL);

fprintf(f, "\t],\n");
fprintf(f, "{\n");

fprintf(f, " \"statecount\": %u,\n", (unsigned) fsm->statecount);

if (fsm_getstart(fsm, &start)) {
fprintf(f, " \"start\": %u,\n", start);
}

{
fsm_state_t start;
int notfirst;

if (!fsm_getstart(fsm, &start)) {
return;
notfirst = 0;

fprintf(f, " \"end\": [ ");
for (i = 0; i < fsm->statecount; i++) {
if (fsm_isend(fsm, i)) {
if (notfirst) {
fprintf(f, ", ");
}

fprintf(f, "%u", i);

notfirst = 1;
}
}
fprintf(f, " ],\n");
}

if (fsm->opt->endleaf != NULL) {
int notfirst;

notfirst = 0;

fprintf(f, " \"endleaf\": [ ");
for (i = 0; i < fsm->statecount; i++) {
if (fsm_isend(fsm, i)) {
if (notfirst) {
fprintf(f, ", ");
}

fprintf(f, "{ %u, ", i);

fsm->opt->endleaf(f, &fsm->states[i], fsm->opt->endleaf_opaque);

fprintf(f, " }");

notfirst = 1;
}
}
fprintf(f, " ],\n");
}

{
int notfirst;

notfirst = 0;

fprintf(f, "\t\"start\": %u\n", start);
fprintf(f, " \"edges\": [\n");
for (i = 0; i < fsm->statecount; i++) {
singlestate(f, fsm, i, &notfirst);
}
fprintf(f, "\n ]\n");
}

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

31 changes: 31 additions & 0 deletions src/re/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,35 @@ endleaf_dot(FILE *f, const void *state_opaque, const void *endleaf_opaque)
return 0;
}

static int
endleaf_json(FILE *f, const void *state_opaque, const void *endleaf_opaque)
{
const struct match *m;

assert(f != NULL);
assert(state_opaque != NULL);
assert(endleaf_opaque == NULL);

(void) endleaf_opaque;

fprintf(f, "[ ");

for (m = state_opaque; m != NULL; m = m->next) {
fprintf(f, "%u", m->i);

if (m->next != NULL) {
fprintf(f, ", ");
}
}

fprintf(f, " ]");

/* TODO: only if comments */
/* TODO: centralise to libfsm/print/json.c */

return 0;
}

int
main(int argc, char *argv[])
{
Expand Down Expand Up @@ -869,6 +898,8 @@ main(int argc, char *argv[])
opt.endleaf = endleaf_c;
} else if (print_fsm == fsm_print_dot) {
opt.endleaf = patterns ? endleaf_dot : NULL;
} else if (print_fsm == fsm_print_json) {
opt.endleaf = patterns ? endleaf_json : NULL;
}

print_fsm(stdout, fsm);
Expand Down