From 1e5f5ecc5f6e75ac28c9b03f10837a7231dbca8e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 14 Nov 2014 10:51:29 +1030 Subject: [PATCH] cdump: add CDUMP() support. This lets you annotate your headers with notes for cdump. Signed-off-by: Rusty Russell --- ccan/cdump/cdump.c | 53 ++++++++++- ccan/cdump/cdump.h | 10 ++ ccan/cdump/test/run-CDUMP.c | 176 ++++++++++++++++++++++++++++++++++++ 3 files changed, 236 insertions(+), 3 deletions(-) create mode 100644 ccan/cdump/test/run-CDUMP.c diff --git a/ccan/cdump/cdump.c b/ccan/cdump/cdump.c index 8ade786c..dad319dc 100644 --- a/ccan/cdump/cdump.c +++ b/ccan/cdump/cdump.c @@ -280,19 +280,30 @@ static bool tok_take_expr(struct parse_state *ps, const char *term) return tok_take(&ps->toks); } +static char *tok_take_expr_str(const tal_t *ctx, + struct parse_state *ps, + const char *term) +{ + const struct token *start = tok_peek(&ps->toks); + + if (!tok_take_expr(ps, term)) + return NULL; + + return string_of_toks(ctx, start, ps->toks - 1); +} + /* [ ... */ static bool tok_take_array(struct parse_state *ps, struct cdump_type **type) { /* This will be some arbitrary expression! */ struct cdump_type *arr = get_type(ps->defs, CDUMP_ARRAY, NULL); - const struct token *start = tok_peek(&ps->toks); - if (!tok_take_expr(ps, "]")) { + arr->u.arr.size = tok_take_expr_str(arr, ps, "]"); + if (!arr->u.arr.size) { complain(ps, "Could not find closing array size ]"); return false; } - arr->u.arr.size = string_of_toks(arr, start, ps->toks - 1); arr->u.arr.type = *type; *type = arr; @@ -350,6 +361,25 @@ static bool tok_take_type(struct parse_state *ps, struct cdump_type **type) return true; } +/* CDUMP */ +static bool tok_maybe_take_cdump_note(const tal_t *ctx, + struct parse_state *ps, const char **note) +{ + *note = NULL; + if (tok_take_if(&ps->toks, "CDUMP")) { + if (!tok_take_if(&ps->toks, "(")) { + complain(ps, "Expected ( after CDUMP"); + return false; + } + *note = tok_take_expr_str(ctx, ps, ")"); + if (!*note) { + complain(ps, "Expected ) after CDUMP("); + return false; + } + } + return true; +} + /* struct|union ... */ static bool tok_take_conglom(struct parse_state *ps, enum cdump_type_kind conglom_kind) @@ -372,6 +402,9 @@ static bool tok_take_conglom(struct parse_state *ps, return false; } + if (!tok_maybe_take_cdump_note(e, ps, &e->note)) + return false; + if (!tok_take_if(&ps->toks, "{")) { complain(ps, "Expected { for struct/union"); return false; @@ -423,6 +456,11 @@ static bool tok_take_conglom(struct parse_state *ps, if (!tok_take_array(ps, &m->type)) return false; } + + /* CDUMP() */ + if (!tok_maybe_take_cdump_note(e->u.members, + ps, &m->note)) + return false; } while (tok_take_if(&ps->toks, ",")); if (!tok_take_if(&ps->toks, ";")) { @@ -458,6 +496,10 @@ static bool tok_take_enum(struct parse_state *ps) return false; } + /* CDUMP() */ + if (!tok_maybe_take_cdump_note(e, ps, &e->note)) + return false; + if (!tok_take_if(&ps->toks, "{")) { complain(ps, "Expected { after enum name"); return false; @@ -479,6 +521,11 @@ static bool tok_take_enum(struct parse_state *ps) complain(ps, "Expected enum value name"); return false; } + + /* CDUMP() */ + if (!tok_maybe_take_cdump_note(e->u.enum_vals, ps, &v->note)) + return false; + if (tok_take_if(&ps->toks, "=")) { v->value = tok_take_until(e, &ps->toks, ",}"); if (!v->value) { diff --git a/ccan/cdump/cdump.h b/ccan/cdump/cdump.h index ede6310e..312767b4 100644 --- a/ccan/cdump/cdump.h +++ b/ccan/cdump/cdump.h @@ -15,6 +15,7 @@ enum cdump_type_kind { struct cdump_member { const char *name; + const char *note; /* const, volatile */ const char *qualifiers; struct cdump_type *type; @@ -22,6 +23,7 @@ struct cdump_member { struct cdump_enum_val { const char *name; + const char *note; /* Either NULL, or whatever follows '=' sign */ const char *value; }; @@ -34,6 +36,7 @@ struct cdump_array { struct cdump_type { enum cdump_type_kind kind; const char *name; + const char *note; union { /* CDUMP_STRUCT / CDUMP_UNION: array */ struct cdump_member *members; @@ -67,6 +70,11 @@ struct cdump_definitions { * into the return. If there is a parse error, it will return NULL and * allocate a problem string for human consumption. * + * Annotations can be attached to structures, unions, enums, members + * and enum values using CDUMP(). This comes after the name (or + * after [] for array member declarations) and usually is removed from + * C compilation using "#define CDUMP(x)". + * * Example: * // Returns name of first field of 'struct @name' in @code. * static const char *first_field_of_struct(const char *code, @@ -88,6 +96,8 @@ struct cdump_definitions { * return NULL; * } * assert(t->kind == CDUMP_STRUCT); + * if (t->note) + * printf("Note on struct %s: %s\n", name, t->note); * return t->u.members[0].name; * } */ diff --git a/ccan/cdump/test/run-CDUMP.c b/ccan/cdump/test/run-CDUMP.c new file mode 100644 index 00000000..8e9783b0 --- /dev/null +++ b/ccan/cdump/test/run-CDUMP.c @@ -0,0 +1,176 @@ +#include +/* Include the C files directly. */ +#include +#include + +int main(void) +{ + struct cdump_definitions *defs; + const struct cdump_type *t, *p; + char *ctx = tal(NULL, char), *problems; + + /* This is how many tests you plan to run */ + plan_tests(111); + + defs = cdump_extract(ctx, "enum foo CDUMP(foo note) { BAR CDUMP(bar note) };", NULL); + ok1(defs); + ok1(tal_parent(defs) == ctx); + + ok1(strmap_empty(&defs->structs)); + ok1(strmap_empty(&defs->unions)); + t = strmap_get(&defs->enums, "foo"); + ok1(t); + ok1(t->kind == CDUMP_ENUM); + ok1(streq(t->note, "foo note")); + ok1(streq(t->name, "foo")); + ok1(tal_count(t->u.enum_vals) == 1); + ok1(streq(t->u.enum_vals[0].name, "BAR")); + ok1(!t->u.enum_vals[0].value); + ok1(streq(t->u.enum_vals[0].note, "bar note")); + + defs = cdump_extract(ctx, "enum foo { BAR CDUMP(bar note) = 7 };", + &problems); + ok1(defs); + ok1(tal_parent(defs) == ctx); + ok1(!problems); + + ok1(strmap_empty(&defs->structs)); + ok1(strmap_empty(&defs->unions)); + t = strmap_get(&defs->enums, "foo"); + ok1(t); + ok1(t->kind == CDUMP_ENUM); + ok1(streq(t->name, "foo")); + ok1(tal_count(t->u.enum_vals) == 1); + ok1(streq(t->u.enum_vals[0].name, "BAR")); + ok1(streq(t->u.enum_vals[0].value, "7")); + ok1(streq(t->u.enum_vals[0].note, "bar note")); + + defs = cdump_extract(ctx, "enum foo {\n" + "BAR CDUMP(bar note) = 7,\n" + "BAZ CDUMP(baz note),\n" + "FUZZ CDUMP(fuzz note) };", + &problems); + ok1(defs); + ok1(tal_parent(defs) == ctx); + ok1(!problems); + + ok1(strmap_empty(&defs->structs)); + ok1(strmap_empty(&defs->unions)); + t = strmap_get(&defs->enums, "foo"); + ok1(t); + ok1(t->kind == CDUMP_ENUM); + ok1(streq(t->name, "foo")); + ok1(t->note == NULL); + ok1(tal_count(t->u.enum_vals) == 3); + ok1(streq(t->u.enum_vals[0].name, "BAR")); + ok1(streq(t->u.enum_vals[0].value, "7")); + ok1(streq(t->u.enum_vals[0].note, "bar note")); + ok1(streq(t->u.enum_vals[1].name, "BAZ")); + ok1(streq(t->u.enum_vals[1].note, "baz note")); + ok1(!t->u.enum_vals[1].value); + ok1(streq(t->u.enum_vals[2].name, "FUZZ")); + ok1(streq(t->u.enum_vals[2].note, "fuzz note")); + ok1(!t->u.enum_vals[2].value); + + defs = cdump_extract(ctx, "struct foo CDUMP(foo note) { int x CDUMP(x note); };", &problems); + ok1(defs); + ok1(tal_parent(defs) == ctx); + ok1(!problems); + + ok1(strmap_empty(&defs->enums)); + ok1(strmap_empty(&defs->unions)); + t = strmap_get(&defs->structs, "foo"); + ok1(t); + ok1(t->kind == CDUMP_STRUCT); + ok1(streq(t->name, "foo")); + ok1(streq(t->note, "foo note")); + ok1(tal_count(t->u.members) == 1); + ok1(streq(t->u.members[0].name, "x")); + ok1(streq(t->u.members[0].note, "x note")); + ok1(t->u.members[0].type->kind == CDUMP_UNKNOWN); + ok1(streq(t->u.members[0].type->name, "int")); + + defs = cdump_extract(ctx, "struct foo { int x[5<< 1] CDUMP(x note); struct foo *next CDUMP(next note); struct unknown **ptrs[10] CDUMP(ptrs note); };", &problems); + ok1(defs); + ok1(tal_parent(defs) == ctx); + ok1(!problems); + + ok1(strmap_empty(&defs->enums)); + ok1(strmap_empty(&defs->unions)); + t = strmap_get(&defs->structs, "foo"); + ok1(t); + ok1(t->kind == CDUMP_STRUCT); + ok1(streq(t->name, "foo")); + ok1(tal_count(t->u.members) == 3); + + ok1(streq(t->u.members[0].name, "x")); + ok1(streq(t->u.members[0].note, "x note")); + ok1(t->u.members[0].type->kind == CDUMP_ARRAY); + ok1(streq(t->u.members[0].type->u.arr.size, "5<< 1")); + ok1(t->u.members[0].type->u.arr.type->kind == CDUMP_UNKNOWN); + ok1(streq(t->u.members[0].type->u.arr.type->name, "int")); + + ok1(streq(t->u.members[1].name, "next")); + ok1(streq(t->u.members[1].note, "next note")); + ok1(t->u.members[1].type->kind == CDUMP_POINTER); + ok1(t->u.members[1].type->u.ptr == t); + + ok1(streq(t->u.members[2].name, "ptrs")); + ok1(streq(t->u.members[2].note, "ptrs note")); + p = t->u.members[2].type; + ok1(p->kind == CDUMP_ARRAY); + ok1(streq(p->u.arr.size, "10")); + p = p->u.arr.type; + ok1(p->kind == CDUMP_POINTER); + p = p->u.ptr; + ok1(p->kind == CDUMP_POINTER); + p = p->u.ptr; + ok1(p->kind == CDUMP_STRUCT); + ok1(streq(p->name, "unknown")); + ok1(p->u.members == NULL); + + /* We don't put undefined structs into definition maps. */ + ok1(!strmap_get(&defs->structs, "unknown")); + + /* unions and comments. */ + defs = cdump_extract(ctx, "#if 0\n" + "/* Normal comment */\n" + "struct foo { int x[5 * 7/* Comment */]CDUMP(x note/*nocomment*/); };\n" + "// One-line comment\n" + "union bar CDUMP(bar note) { enum sometype x CDUMP(x note// Comment\n" + "); union yun// Comment\n" + "y;};\n" + "#endif", &problems); + ok1(defs); + ok1(tal_parent(defs) == ctx); + ok1(!problems); + t = strmap_get(&defs->structs, "foo"); + ok1(t); + ok1(t->note == NULL); + ok1(tal_count(t->u.members) == 1); + ok1(streq(t->u.members[0].name, "x")); + ok1(streq(t->u.members[0].note, "x note")); + ok1(t->u.members[0].type->kind == CDUMP_ARRAY); + ok1(streq(t->u.members[0].type->u.arr.size, "5 * 7")); + ok1(t->u.members[0].type->u.arr.type->kind == CDUMP_UNKNOWN); + ok1(streq(t->u.members[0].type->u.arr.type->name, "int")); + + t = strmap_get(&defs->unions, "bar"); + ok1(t); + ok1(streq(t->note, "bar note")); + + ok1(tal_count(t->u.members) == 2); + ok1(streq(t->u.members[0].name, "x")); + ok1(streq(t->u.members[0].note, "x note")); + ok1(t->u.members[0].type->kind == CDUMP_ENUM); + ok1(streq(t->u.members[0].type->name, "sometype")); + ok1(!t->u.members[0].type->u.enum_vals); + ok1(streq(t->u.members[1].name, "y")); + ok1(t->u.members[1].note == NULL); + ok1(t->u.members[1].type->kind == CDUMP_UNION); + ok1(streq(t->u.members[1].type->name, "yun")); + ok1(!t->u.members[1].type->u.members); + + /* This exits depending on whether all tests passed */ + return exit_status(); +} -- 2.39.2