Commit 158691ae authored by Rusty Russell's avatar Rusty Russell

cdump: new module.

Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
parent 17aa322a
......@@ -41,6 +41,7 @@ MODS_WITH_SRC := antithread \
btree \
bytestring \
ccan_tokenizer \
cdump \
charset \
ciniparser \
crc \
......
../../licenses/BSD-MIT
\ No newline at end of file
#include "config.h"
#include <stdio.h>
#include <string.h>
/**
* cdump - routines to parse simple C structures.
*
* This code is designed to produce data structures summarizing C code.
* It only operates on simple, well-formed C code (eg. specific headers
* which you want to autogenerate from), but it should be fairly easy to
* enhance if desired.
*
* Author: Rusty Russell <rusty@rustcorp.com.au>
* License: BSD-MIT
*
* Example:
* // Creates a simple print function for a structure.
* #include <ccan/cdump/cdump.h>
* #include <ccan/tal/grab_file/grab_file.h>
* #include <ccan/err/err.h>
*
* static void print_as(const char *fmt, const char *member_name)
* {
* printf("\tprintf(\"%%s:%s\\n\", \"%s\", s->%s);\n",
* fmt, member_name, member_name);
* }
*
* int main(int argc, char *argv[])
* {
* char *code, *problems;
* struct cdump_definitions *defs;
* int i, j;
*
* // Read code from stdin.
* code = grab_file(NULL, NULL);
*
* defs = cdump_extract(NULL, code, &problems);
* if (!defs)
* errx(1, "Parsing stdin: %s", problems);
*
* for (i = 1; i < argc; i++) {
* struct cdump_type *t = strmap_get(&defs->structs, argv[i]);
* if (!t)
* errx(1, "Could not find struct %s", argv[i]);
*
* printf("void print_struct_%s(const struct %s *s)\n"
* "{\n", argv[i], argv[i]);
* for (j = 0; j < tal_count(t->u.members); j++) {
* const struct cdump_member *m = t->u.members + j;
* switch (m->type->kind) {
* case CDUMP_STRUCT:
* case CDUMP_UNION:
* case CDUMP_ARRAY:
* // Too hard for this simple example.
* printf("\tprintf(\"%%s:???\\n\", \"%s\");\n",
* m->name);
* break;
* case CDUMP_ENUM:
* print_as("%i", m->name);
* break;
* case CDUMP_POINTER:
* print_as("%p", m->name);
* break;
* case CDUMP_UNKNOWN:
* if (!strcmp(m->type->name, "int"))
* print_as("%i", m->name);
* else if (!strcmp(m->type->name, "long int"))
* print_as("%li", m->name);
* else if (!strcmp(m->type->name, "unsigned int"))
* print_as("%u", m->name);
* // etc...
* break;
* }
* }
* printf("}\n");
* }
* return 0;
* }
*/
int main(int argc, char *argv[])
{
/* Expect exactly one argument */
if (argc != 2)
return 1;
if (strcmp(argv[1], "depends") == 0) {
printf("ccan/tal\n");
printf("ccan/tal/str\n");
printf("ccan/strmap\n");
return 0;
}
return 1;
}
/* MIT (BSD) license - see LICENSE file for details */
#include "cdump.h"
#include <ccan/tal/str/str.h>
#include <assert.h>
struct token {
const char *p;
size_t len;
};
static void add_token(struct token **toks, const char *p, size_t len)
{
size_t n = tal_count(*toks);
tal_resize(toks, n+1);
(*toks)[n].p = p;
(*toks)[n].len = len;
}
/* Simplified tokenizer: comments and preproc directives removed,
identifiers are a token, others are single char tokens. */
static struct token *tokenize(const void *ctx, const char *code)
{
unsigned int i, len, tok_start = -1;
bool start_of_line = true;
struct token *toks = tal_arr(ctx, struct token, 0);
for (i = 0; code[i]; i += len) {
if (code[i] == '#' && start_of_line) {
/* Preprocessor line. */
len = strcspn(code+i, "\n");
} else if (code[i] == '/' && code[i+1] == '/') {
/* One line comment. */
len = strcspn(code+i, "\n");
if (tok_start != -1U) {
add_token(&toks, code+tok_start, i - tok_start);
tok_start = -1U;
}
} else if (code[i] == '/' && code[i+1] == '*') {
/* Multi-line comment. */
const char *end = strstr(code+i+2, "*/");
len = (end + 2) - (code + i);
if (!end)
len = strlen(code + i);
if (tok_start != -1U) {
add_token(&toks, code+tok_start, i - tok_start);
tok_start = -1U;
}
} else if (cisalnum(code[i]) || code[i] == '_') {
/* Identifier or part thereof */
if (tok_start == -1U)
tok_start = i;
len = 1;
} else if (!cisspace(code[i])) {
/* Punctuation: treat as single char token. */
if (tok_start != -1U) {
add_token(&toks, code+tok_start, i - tok_start);
tok_start = -1U;
}
add_token(&toks, code+i, 1);
len = 1;
} else {
/* Whitespace. */
if (tok_start != -1U) {
add_token(&toks, code+tok_start, i - tok_start);
tok_start = -1U;
}
len = 1;
}
if (code[i] == '\n')
start_of_line = true;
else if (!cisspace(code[i]))
start_of_line = false;
}
/* Add terminating NULL. */
tal_resizez(&toks, tal_count(toks) + 1);
return toks;
}
struct parse_state {
const char *code;
const struct token *toks;
struct cdump_definitions *defs;
char *complaints;
};
static bool tok_is(const struct token **toks, const char *target)
{
return (*toks)->p && (*toks)->len == strlen(target)
&& memcmp((*toks)->p, target, (*toks)->len) == 0;
}
static const struct token *tok_peek(const struct token **toks)
{
if (toks[0]->p)
return toks[0];
return NULL;
}
static const struct token *tok_take(const struct token **toks)
{
if (!toks[0]->p)
return NULL;
return (*toks)++;
}
static const struct token *tok_take_if(const struct token **toks,
const char *target)
{
if (tok_is(toks, target))
return tok_take(toks);
return NULL;
}
static const char *tok_take_ident(const tal_t *ctx, const struct token **toks)
{
const struct token *t = tok_peek(toks);
if (!t)
return NULL;
if (strspn(t->p, "_0123456789"
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ") < t->len)
return NULL;
t = tok_take(toks);
return tal_strndup(ctx, t->p, t->len);
}
static char *string_of_toks(const tal_t *ctx,
const struct token *first,
const struct token *until)
{
const struct token *end = until - 1;
return tal_strndup(ctx, first->p, end->p - first->p + end->len);
}
static char *tok_take_until(const tal_t *ctx,
const struct token **toks,
const char *delims)
{
const struct token *t, *start;
start = tok_peek(toks);
while ((t = tok_peek(toks)) != NULL) {
/* If this contains a delimiter, copy up to prev token. */
if (strcspn(t->p, delims) < t->len)
return string_of_toks(ctx, start, t);
tok_take(toks);
};
/* EOF without finding delimiter */
return NULL;
}
static bool type_defined(const struct cdump_type *t)
{
switch (t->kind) {
case CDUMP_STRUCT:
case CDUMP_UNION:
return (t->u.members != NULL);
case CDUMP_ENUM:
return (t->u.enum_vals != NULL);
/* These shouldn't happen; we don't try to define them. */
case CDUMP_UNKNOWN:
case CDUMP_ARRAY:
case CDUMP_POINTER:
break;
}
abort();
}
/* May allocate a new type if not already found (steals @name) */
static struct cdump_type *get_type(struct cdump_definitions *defs,
enum cdump_type_kind kind,
const char *name)
{
struct cdump_map *m;
struct cdump_type *t;
switch (kind) {
case CDUMP_STRUCT:
m = &defs->structs;
break;
case CDUMP_UNION:
m = &defs->unions;
break;
case CDUMP_ENUM:
m = &defs->enums;
break;
case CDUMP_UNKNOWN:
case CDUMP_ARRAY:
case CDUMP_POINTER:
m = NULL;
}
/* Do we already have it? */
if (m) {
t = strmap_get(m, name);
if (t)
return t;
}
t = tal(defs, struct cdump_type);
t->kind = kind;
t->name = name ? tal_steal(t, name) : NULL;
/* These are actually the same, but be thorough */
t->u.members = NULL;
t->u.enum_vals = NULL;
if (m)
strmap_add(m, t->name, t);
return t;
}
static void complain(struct parse_state *ps, const char *complaint)
{
unsigned int linenum;
const char *p = ps->code;
for (linenum = 1; p < ps->toks[0].p; linenum++) {
p = strchr(p+1, '\n');
if (!p)
break;
}
tal_append_fmt(&ps->complaints,
"Line %u: '%.*s': %s\n",
linenum, (int)ps->toks[0].len,
ps->toks[0].p, complaint);
}
static void tok_take_unknown_statement(struct parse_state *ps)
{
complain(ps, "Ignoring unknown statement until next semicolon");
tal_free(tok_take_until(NULL, &ps->toks, ";"));
tok_take_if(&ps->toks, ";");
}
/* [ ... */
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);
arr->u.arr.size = tok_take_until(arr, &ps->toks, "]");
if (!arr->u.arr.size) {
complain(ps, "Could not find closing array size ]");
return false;
}
arr->u.arr.type = *type;
*type = arr;
/* Swallow ] */
tok_take(&ps->toks);
return true;
}
static struct cdump_type *ptr_of(struct parse_state *ps,
const struct cdump_type *ptr_to)
{
struct cdump_type *ptr = get_type(ps->defs, CDUMP_POINTER, NULL);
ptr->u.ptr = ptr_to;
return ptr;
}
static bool tok_take_type(struct parse_state *ps, struct cdump_type **type)
{
const char *name;
const struct token *types;
enum cdump_type_kind kind;
/* Ignoring weird typedefs, only these can be combined. */
types = ps->toks;
while (tok_take_if(&ps->toks, "int")
|| tok_take_if(&ps->toks, "long")
|| tok_take_if(&ps->toks, "short")
|| tok_take_if(&ps->toks, "double")
|| tok_take_if(&ps->toks, "float")
|| tok_take_if(&ps->toks, "char")
|| tok_take_if(&ps->toks, "signed")
|| tok_take_if(&ps->toks, "unsigned"));
/* Did we get some? */
if (ps->toks != types) {
name = string_of_toks(NULL, types, tok_peek(&ps->toks));
kind = CDUMP_UNKNOWN;
} else {
/* Try normal types (or simple typedefs, etc). */
if (tok_take_if(&ps->toks, "struct")) {
kind = CDUMP_STRUCT;
} else if (tok_take_if(&ps->toks, "union")) {
kind = CDUMP_UNION;
} else if (tok_take_if(&ps->toks, "enum")) {
kind = CDUMP_ENUM;
} else
kind = CDUMP_UNKNOWN;
name = tok_take_ident(ps->defs, &ps->toks);
if (!name) {
complain(ps, "Invalid typename");
return false;
}
}
*type = get_type(ps->defs, kind, name);
return true;
}
/* struct|union ... */
static bool tok_take_conglom(struct parse_state *ps,
enum cdump_type_kind conglom_kind)
{
struct cdump_type *e;
const char *name;
size_t n;
assert(conglom_kind == CDUMP_STRUCT || conglom_kind == CDUMP_UNION);
name = tok_take_ident(ps->defs, &ps->toks);
if (!name) {
complain(ps, "Invalid struct/union name");
return false;
}
e = get_type(ps->defs, conglom_kind, name);
if (type_defined(e)) {
complain(ps, "Type already defined");
return false;
}
if (!tok_take_if(&ps->toks, "{")) {
complain(ps, "Expected { for struct/union");
return false;
}
e->u.members = tal_arr(e, struct cdump_member, n = 0);
while (!tok_is(&ps->toks, "}")) {
struct cdump_type *basetype;
const struct token *quals;
unsigned int num_quals = 0;
/* Anything can have these prepended. */
quals = ps->toks;
while (tok_take_if(&ps->toks, "const")
|| tok_take_if(&ps->toks, "volatile"))
num_quals++;
/* eg. "struct foo" or "varint_t" */
if (!tok_take_type(ps, &basetype)) {
complain(ps, "Expected typename inside struct/union");
return false;
}
do {
struct cdump_member *m;
tal_resize(&e->u.members, n+1);
m = &e->u.members[n++];
m->type = basetype;
if (num_quals) {
m->qualifiers
= string_of_toks(e, quals,
quals + num_quals);
} else
m->qualifiers = NULL;
/* May have multiple asterisks. */
while (tok_take_if(&ps->toks, "*"))
m->type = ptr_of(ps, m->type);
m->name = tok_take_ident(e, &ps->toks);
if (!m->name) {
complain(ps, "Expected name for member");
return false;
}
/* May be an array. */
while (tok_take_if(&ps->toks, "[")) {
if (!tok_take_array(ps, &m->type))
return false;
}
} while (tok_take_if(&ps->toks, ","));
if (!tok_take_if(&ps->toks, ";")) {
complain(ps, "Expected ; at end of member");
return false;
}
}
if (tok_take_if(&ps->toks, "}") && tok_take_if(&ps->toks, ";"))
return true;
complain(ps, "Expected }; at end of struct/union");
return false;
}
/* enum ... */
static bool tok_take_enum(struct parse_state *ps)
{
size_t n = 0;
struct cdump_type *e;
const char *name;
name = tok_take_ident(ps->defs, &ps->toks);
if (!name) {
complain(ps, "Expected enum name");
return false;
}
e = get_type(ps->defs, CDUMP_ENUM, name);
/* Duplicate name? */
if (type_defined(e)) {
complain(ps, "enum already defined");
return false;
}
if (!tok_take_if(&ps->toks, "{")) {
complain(ps, "Expected { after enum name");
return false;
}
e->u.enum_vals = tal_arr(e, struct cdump_enum_val, n);
do {
struct cdump_enum_val *v;
tal_resize(&e->u.enum_vals, n+1);
v = &e->u.enum_vals[n++];
v->name = tok_take_ident(e, &ps->toks);
if (!v->name) {
complain(ps, "Expected enum value name");
return false;
}
if (tok_take_if(&ps->toks, "=")) {
v->value = tok_take_until(e, &ps->toks, ",}");
if (!v->value) {
complain(ps, "Expected , or } to end value");
return false;
}
} else
v->value = NULL;
} while (tok_take_if(&ps->toks, ","));
if (tok_take_if(&ps->toks, "}") && tok_take_if(&ps->toks, ";"))
return true;
complain(ps, "Expected }; at end of enum");
return false;
}
static bool gather_undefines(const char *name,
struct cdump_type *t,
struct cdump_map *undefs)
{
if (!type_defined(t))
strmap_add(undefs, name, t);
return true;
}
static bool remove_from_map(const char *name,
struct cdump_type *t,
struct cdump_map *map)
{
strmap_del(map, name, NULL);
return true;
}
static void remove_undefined(struct cdump_map *map)
{
struct cdump_map undefs;
/* We can't delete inside iterator, so gather all the undefs
* then remove them. */
strmap_init(&undefs);
strmap_iterate(map, gather_undefines, &undefs);
strmap_iterate(&undefs, remove_from_map, map);
strmap_clear(&undefs);
}
static void destroy_definitions(struct cdump_definitions *defs)
{
strmap_clear(&defs->enums);
strmap_clear(&defs->structs);
strmap_clear(&defs->unions);
}
/* Simple LL(1) parser, inspired by Tridge's genstruct.pl. */
struct cdump_definitions *cdump_extract(const tal_t *ctx, const char *code,
char **complaints)
{
struct parse_state ps;
const struct token *toks;
ps.defs = tal(ctx, struct cdump_definitions);
ps.complaints = tal_strdup(ctx, "");
ps.code = code;
strmap_init(&ps.defs->enums);
strmap_init(&ps.defs->structs);
strmap_init(&ps.defs->unions);
tal_add_destructor(ps.defs, destroy_definitions);
toks = ps.toks = tokenize(ps.defs, code);
while (tok_peek(&ps.toks)) {
if (tok_take_if(&ps.toks, "struct")) {
if (!tok_take_conglom(&ps, CDUMP_STRUCT))
goto fail;
} else if (tok_take_if(&ps.toks, "union")) {
if (!tok_take_conglom(&ps, CDUMP_UNION))
goto fail;
} else if (tok_take_if(&ps.toks, "enum")) {
if (!tok_take_enum(&ps))
goto fail;
} else
tok_take_unknown_statement(&ps);
}
/* Now, remove any undefined types! */
remove_undefined(&ps.defs->enums);
remove_undefined(&ps.defs->structs);
remove_undefined(&ps.defs->unions);
tal_free(toks);
out:
if (streq(ps.complaints, ""))
ps.complaints = tal_free(ps.complaints);
if (complaints)
*complaints = ps.complaints;
else
tal_free(ps.complaints);
return ps.defs;
fail:
ps.defs = tal_free(ps.defs);
goto out;
}
/* MIT (BSD) license - see LICENSE file for details */
#ifndef CCAN_CDUMP_H
#define CCAN_CDUMP_H
#include <ccan/strmap/strmap.h>
#include <ccan/tal/tal.h>
enum cdump_type_kind {
CDUMP_STRUCT,
CDUMP_UNION,
CDUMP_ENUM,
CDUMP_ARRAY,
CDUMP_POINTER,
CDUMP_UNKNOWN
};
struct cdump_member {
const char *name;
/* const, volatile */
const char *qualifiers;
struct cdump_type *type;
};
struct cdump_enum_val {
const char *name;
/* Either NULL, or whatever follows '=' sign */
const char *value;
};
struct cdump_array {
const char *size;
struct cdump_type *type;
};
struct cdump_type {
enum cdump_type_kind kind;
const char *name;
union {
/* CDUMP_STRUCT / CDUMP_UNION: array */
struct cdump_member *members;
/* CDUMP_ENUM: array */
struct cdump_enum_val *enum_vals;
/* CDUMP_ARRAY */
struct cdump_array arr;
/* CDUMP_POINTER */
const struct cdump_type *ptr;
} u;
};
/* The map of typenames to definitions */
struct cdump_map {
STRMAP_MEMBERS(struct cdump_type *);
};
struct cdump_definitions {
struct cdump_map enums;
struct cdump_map structs;
struct cdump_map unions;
};
/**
* cdump_extract - extract definitions from simple C code.
* @ctx: context to tal() the return and @problems from (or NULL)
* @code: a nul-terminated string of C definitions
* @problems: a pointer to a char * to report problems (or NULL)
*
* This function parses @code and extracts enum, struct and union definitions
* into the return. If there is a parse error, it will return NULL and
* allocate a problem string for human consumption.
*
* Example:
* // Returns name of first field of 'struct @name' in @code.
* static const char *first_field_of_struct(const char *code,
* const char *name)
* {
* char *problems;
* struct cdump_definitions *defs;
* struct cdump_type *t;
*
* defs = cdump_extract(NULL, code, &problems);
* if (!defs) {
* fprintf(stderr, "%s", problems);
* tal_free(problems);
* return NULL;
* }
* t = strmap_get(&defs->structs, name);
* if (!t) {
* fprintf(stderr, "Couldn't find struct %s", name);
* return NULL;
* }
* assert(t->kind == CDUMP_STRUCT);
* return t->u.members[0].name;
* }
*/
struct cdump_definitions *cdump_extract(const tal_t *ctx, const char *code,
char **problems);
#endif /* CCAN_CDUMP_H */
#include <ccan/cdump/cdump.h>
/* Include the C files directly. */
#include <ccan/cdump/cdump.c>
#include <ccan/tap/tap.h>
int main(void)
{
struct cdump_definitions *defs;
const struct cdump_type *t, *t2;
char *ctx = tal(NULL, char), *problems;
/* This is how many tests you plan to run */
plan_tests(16);
defs = cdump_extract(ctx, "struct foo { struct bar *bar; };\n"
"struct bar { int x; };", &problems);
ok1(defs);
ok1(tal_parent(defs) == ctx);
ok1(!problems);
t = strmap_get(&defs->structs, "foo");
ok1(t);
t2 = strmap_get(&defs->structs, "bar");
ok1(t2);
ok1(t2->kind == CDUMP_STRUCT);
ok1(streq(t2->name, "bar"));
ok1(tal_count(t2->u.members) == 1);
ok1(t2->u.members[0].type->kind == CDUMP_UNKNOWN);
ok1(streq(t2->u.members[0].type->name, "int"));
ok1(t->kind == CDUMP_STRUCT);
ok1(streq(t->name, "foo"));
ok1(tal_count(t->u.members) == 1);
ok1(streq(t->u.members[0].name, "bar"));
ok1(t->u.members[0].type->kind == CDUMP_POINTER);
ok1(t->u.members[0].type->u.ptr == t2);
tal_free(ctx);
/* This exits depending on whether all tests passed */
return exit_status();
}
#include <ccan/cdump/cdump.h>
/* Include the C files directly. */
#include <ccan/cdump/cdump.c>
#include <ccan/tap/tap.h>
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(63);
defs = cdump_extract(ctx,
"struct foo {\n"
" long l;\n"
" long int li;\n"
" unsigned long *ulp;\n"
" unsigned long int *ulip;\n"
"};", &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) == 4);
ok1(streq(t->u.members[0].name, "l"));
p = t->u.members[0].type;
ok1(p->kind == CDUMP_UNKNOWN);
ok1(streq(p->name, "long"));
ok1(streq(t->u.members[1].name, "li"));
p = t->u.members[1].type;
ok1(p->kind == CDUMP_UNKNOWN);
ok1(streq(p->name, "long int"));
ok1(streq(t->u.members[2].name, "ulp"));
p = t->u.members[2].type;
ok1(p->kind == CDUMP_POINTER);
p = p->u.ptr;
ok1(p->kind == CDUMP_UNKNOWN);
ok1(streq(p->name, "unsigned long"));
ok1(streq(t->u.members[3].name, "ulip"));
p = t->u.members[3].type;
ok1(p->kind == CDUMP_POINTER);
p = p->u.ptr;
ok1(p->kind == CDUMP_UNKNOWN);
ok1(streq(p->name, "unsigned long int"));
defs = cdump_extract(ctx,
"struct foo {\n"
" volatile long vl;\n"
" const long cl;\n"
" volatile const long long int *vclli;\n"
"};", &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, "vl"));
ok1(streq(t->u.members[0].qualifiers, "volatile"));
p = t->u.members[0].type;
ok1(p->kind == CDUMP_UNKNOWN);
ok1(streq(p->name, "long"));
ok1(streq(t->u.members[1].name, "cl"));
ok1(streq(t->u.members[1].qualifiers, "const"));
p = t->u.members[1].type;
ok1(p->kind == CDUMP_UNKNOWN);
ok1(streq(p->name, "long"));
ok1(streq(t->u.members[2].name, "vclli"));
ok1(streq(t->u.members[2].qualifiers, "volatile const"));
p = t->u.members[2].type;
ok1(p->kind == CDUMP_POINTER);
p = p->u.ptr;
ok1(p->kind == CDUMP_UNKNOWN);
ok1(streq(p->name, "long long int"));
defs = cdump_extract(ctx,
"struct foo {\n"
" volatile struct bar *a, b;\n"
"};", &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) == 2);
ok1(streq(t->u.members[0].name, "a"));
ok1(streq(t->u.members[0].qualifiers, "volatile"));
p = t->u.members[0].type;
ok1(p->kind == CDUMP_POINTER);
p = p->u.ptr;
ok1(p->kind == CDUMP_STRUCT);
ok1(streq(p->name, "bar"));
ok1(streq(t->u.members[1].name, "b"));
ok1(streq(t->u.members[1].qualifiers, "volatile"));
p = t->u.members[1].type;
ok1(p->kind == CDUMP_STRUCT);
ok1(streq(p->name, "bar"));
tal_free(ctx);
/* This exits depending on whether all tests passed */
return exit_status();
}
#include <ccan/cdump/cdump.h>
/* Include the C files directly. */
#include <ccan/cdump/cdump.c>
#include <ccan/tap/tap.h>
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(94);
defs = cdump_extract(ctx, "enum foo { BAR };", 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->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);
defs = cdump_extract(ctx, "enum foo { BAR = 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"));
defs = cdump_extract(ctx, "enum foo { BAR = 7, BAZ, FUZZ };", &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) == 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[1].name, "BAZ"));
ok1(!t->u.enum_vals[1].value);
ok1(streq(t->u.enum_vals[2].name, "FUZZ"));
ok1(!t->u.enum_vals[2].value);
defs = cdump_extract(ctx, "struct foo { int x; };", &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) == 1);
ok1(streq(t->u.members[0].name, "x"));
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]; struct foo *next; struct unknown **ptrs[10]; };", &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(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(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"));
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 */]; };\n"
"// One-line comment\n"
"union bar { enum sometype x; 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(tal_count(t->u.members) == 1);
ok1(streq(t->u.members[0].name, "x"));
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(tal_count(t->u.members) == 2);
ok1(streq(t->u.members[0].name, "x"));
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].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();
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment