Skip to content

Commit

Permalink
Add explicit enumerator defaults
Browse files Browse the repository at this point in the history
- Set trough the @default_literal annotation on enumerators
- Add the default_enumerator pointer in idl_enum, which is set
  to the enumerator with @default_literal annotation or the first
  if none have the annotation
- Add unittests

Signed-off-by: Martijn Reicher <martijn.reicher@adlinktech.com>
  • Loading branch information
reicheratwork authored and k0ekk0ek committed Sep 24, 2021
1 parent e4ec4da commit d7a7abb
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 1 deletion.
3 changes: 3 additions & 0 deletions src/idl/include/idl/tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
/* if no explicit default is specified and range is not covered */
#define IDL_IMPLICIT_DEFAULT_CASE_LABEL (IDL_DEFAULT_CASE_LABEL | 2u)
#define IDL_ENUMERATOR (1llu<<26)
#define IDL_DEFAULT_ENUMERATOR (IDL_ENUMERATOR | 1u)
#define IDL_IMPLICIT_DEFAULT_ENUMERATOR (IDL_DEFAULT_ENUMERATOR | 2u)
#define IDL_DECLARATOR (1llu<<25)
/* annotations */
#define IDL_ANNOTATION (1llu<<24)
Expand Down Expand Up @@ -387,6 +389,7 @@ struct idl_enum {
idl_node_t node;
struct idl_name *name;
idl_enumerator_t *enumerators;
idl_enumerator_t *default_enumerator;
IDL_ANNOTATABLE(idl_extensibility_t) extensibility;
};

Expand Down
24 changes: 24 additions & 0 deletions src/idl/src/annotation.c
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,25 @@ annotate_default(
return IDL_RETCODE_OK;
}

static idl_retcode_t
annotate_default_literal(
idl_pstate_t *pstate,
idl_annotation_appl_t *annotation_appl,
idl_node_t *node)
{
assert(pstate);

if (!idl_is_enumerator(node)) {
idl_error(pstate, idl_location(annotation_appl),
"@default_literal can only be assigned to enumerators");
return IDL_RETCODE_SEMANTIC_ERROR;
}

node->mask |= IDL_DEFAULT_ENUMERATOR;

return IDL_RETCODE_OK;
}

static idl_retcode_t
annotate_nested(
idl_pstate_t *pstate,
Expand Down Expand Up @@ -558,6 +577,11 @@ static const idl_builtin_annotation_t annotations[] = {
"<p>Specify the value with which the annotated member should be default"
"initialized.</p>",
.callback = &annotate_default },
{ .syntax = "@annotation default_literal { };",
.summary =
"<p>Explicity sets the default value for an enum to the annotated enumerator"
"instead of the first entry.</p>",
.callback = &annotate_default_literal },
#if 0
{ .syntax = "@annotation must_understand { boolean value default TRUE; };",
.summary =
Expand Down
22 changes: 21 additions & 1 deletion src/idl/src/tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -2029,6 +2029,23 @@ idl_create_enum(
ret = IDL_RETCODE_SEMANTIC_ERROR;
goto err_clash;
}
if (idl_mask(e1) == IDL_DEFAULT_ENUMERATOR) {
if (node->default_enumerator) {
idl_error(pstate, idl_location(e1),
"Assigning default to enumerator '%s' clashes with '%s' already being set as default.",
e1->name->identifier, node->default_enumerator->name->identifier);
ret = IDL_RETCODE_SEMANTIC_ERROR;
goto err_defaults;
} else {
node->default_enumerator = e1;
}
}
}

//fallback to the first entry
if (!node->default_enumerator) {
node->default_enumerator = enumerators;
node->default_enumerator->node.mask = IDL_IMPLICIT_DEFAULT_ENUMERATOR;
}

if ((ret = idl_declare(pstate, kind, name, node, NULL, NULL)))
Expand All @@ -2038,6 +2055,7 @@ idl_create_enum(
return IDL_RETCODE_OK;
err_declare:
err_clash:
err_defaults:
free(node);
err_alloc:
return ret;
Expand Down Expand Up @@ -2075,7 +2093,9 @@ static void *iterate_enumerator(const void *ptr, const void *cur)
static const char *describe_enumerator(const void *ptr)
{
(void)ptr;
assert(idl_mask(ptr) == IDL_ENUMERATOR);
assert(idl_mask(ptr) == IDL_ENUMERATOR
|| idl_mask(ptr) == IDL_DEFAULT_ENUMERATOR
|| idl_mask(ptr) == IDL_IMPLICIT_DEFAULT_ENUMERATOR);
return "enumerator";
}

Expand Down
57 changes: 57 additions & 0 deletions src/idl/tests/annotation.c
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,63 @@ CU_Test(idl_annotation, idl_default)

}

typedef struct enum_default_test {
const char *str;
idl_retcode_t ret;
uint32_t default_index;
const char *default_name;
uint32_t default_mask;
} enum_default_test_t;

static void test_enum_default(enum_default_test_t test) {
idl_pstate_t *pstate = NULL;
idl_retcode_t ret = parse_string(IDL_FLAG_ANNOTATIONS, test.str, &pstate);
CU_ASSERT_EQUAL(ret, test.ret);
if (test.ret == ret
&& ret == IDL_RETCODE_OK) {
CU_ASSERT_PTR_NOT_NULL_FATAL(pstate);
assert(pstate);
idl_enum_t *e = (idl_enum_t *)pstate->root;
CU_ASSERT(idl_is_enum(e));
if (idl_is_enum(e)) {
assert(e);
idl_enumerator_t *en = e->default_enumerator;
CU_ASSERT_PTR_NOT_NULL_FATAL(en);
if (en) {
CU_ASSERT_EQUAL(en->value.value, test.default_index);
CU_ASSERT_EQUAL(idl_mask(en), test.default_mask);
CU_ASSERT_STRING_EQUAL(en->name->identifier, test.default_name);
}

IDL_FOREACH(en, e->enumerators) {
if (en == e->default_enumerator) {
CU_ASSERT_EQUAL(idl_mask(en), test.default_mask);
} else {
CU_ASSERT_EQUAL(idl_mask(en), IDL_ENUMERATOR);
}
}
}

idl_delete_pstate(pstate);
}
}

CU_Test(idl_annotation, default_literal)
{
static const enum_default_test_t tests[] = {
{"enum e { e_0, e_1, e_2, e_3 };", IDL_RETCODE_OK, 0, "e_0", IDL_IMPLICIT_DEFAULT_ENUMERATOR }, //implicit defaults to the first entry
{"enum e { e_0, @default_literal e_1, e_2, e_3 };", IDL_RETCODE_OK, 1, "e_1", IDL_DEFAULT_ENUMERATOR }, //setting an explicit default through the annotation
{"enum e { @value(123) e_0, @default_literal e_1, e_2, e_3 };", IDL_RETCODE_OK, 124, "e_1", IDL_DEFAULT_ENUMERATOR }, //starting at a different id
{"enum e { e_0, e_1, @default_literal @default_literal e_2, e_3 };", IDL_RETCODE_OK, 2, "e_2", IDL_DEFAULT_ENUMERATOR }, //re-annotate
{"enum e { e_0, @default_literal e_1, @default_literal e_2, e_3 };", IDL_RETCODE_SEMANTIC_ERROR, 0, NULL, 0 }, //more than one enumerator set as default
{"struct s { @default_literal long l; double d; };\n", IDL_RETCODE_SEMANTIC_ERROR, 0, NULL, 0 }, //this annotation is only allowed on enumerator fields
};

for (size_t i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) {
test_enum_default(tests[i]);
}
}

CU_Test(idl_annotation, key)
{
idl_retcode_t ret;
Expand Down

0 comments on commit d7a7abb

Please sign in to comment.