Skip to content

Commit

Permalink
Implement parsing of HEP-1 Value Annotations
Browse files Browse the repository at this point in the history
This implements the parsing part of the HEP-1: hipack_value_t is extended to
contain a dictionary (which gets used as a set) of annotations, which gets
initialized and populated lazily, to avoid creating an additional dictionary
per value when no annotations are present.
  • Loading branch information
aperezdc committed Dec 7, 2015
1 parent 582bf54 commit b6f267d
Show file tree
Hide file tree
Showing 4 changed files with 198 additions and 50 deletions.
27 changes: 27 additions & 0 deletions hipack-dict.c
Expand Up @@ -204,6 +204,33 @@ hipack_dict_get (const hipack_dict_t *dict,
}


void
hipack_dict_del (hipack_dict_t *dict,
const hipack_string_t *key)
{
assert (dict);
assert (key);

uint32_t hash_val = hipack_string_hash (key) % dict->size;
for (hipack_dict_node_t *node = dict->nodes[hash_val]; node; node = node->next) {
if (hipack_string_equal (node->key, key)) {
hipack_dict_node_t *prev_node = node->prev_node;
hipack_dict_node_t *next_node = node->next_node;

if (prev_node) prev_node->next_node = next_node;
else dict->first = next_node;
if (next_node) next_node->prev_node = prev_node;

dict->nodes[hash_val] = node->next;
dict->count--;

free_node (node);
return;
}
}
}


hipack_value_t*
hipack_dict_first (const hipack_dict_t *dict,
const hipack_string_t **key)
Expand Down
128 changes: 79 additions & 49 deletions hipack-parser.c
Expand Up @@ -44,7 +44,7 @@ struct parser {
#define DUMMY ) /* Makes autoindentation work. */
#undef DUMMY

#define DUMMY_VALUE ((hipack_value_t) { .type = HIPACK_BOOL })
#define DUMMY_VALUE ((hipack_value_t) { .type = HIPACK_BOOL, .annot = NULL })


static hipack_value_t parse_value (P, S);
Expand Down Expand Up @@ -310,8 +310,8 @@ parse_key (P, S)
}


static hipack_value_t
parse_string (P, S)
static void
parse_string (P, hipack_value_t *result, S)
{
hipack_string_t *hstr = NULL;
uint32_t alloc_size = 0;
Expand Down Expand Up @@ -353,21 +353,19 @@ parse_string (P, S)
}

matchchar (p, '"', "unterminated string value", CHECK_OK);
return (hipack_value_t) {
.type = HIPACK_STRING,
.v_string = hstr ? hstr : hipack_string_new_from_lstring ("", 0)
};
result->type = HIPACK_STRING;
result->v_string = hstr ? hstr : hipack_string_new_from_lstring ("", 0);
return;

error:
hipack_string_free (hstr);
return DUMMY_VALUE;
return;
}


static hipack_value_t
parse_list (P, S)
static void
parse_list (P, hipack_value_t *result, S)
{
hipack_value_t value = DUMMY_VALUE;
hipack_list_t *list = NULL;
uint32_t alloc_size = 0;
uint32_t size = 0;
Expand All @@ -376,10 +374,9 @@ parse_list (P, S)
skipwhite (p, CHECK_OK);

while (p->look != ']') {
value = parse_value (p, CHECK_OK);
hipack_value_t value = parse_value (p, CHECK_OK);
list = list_resize (list, &alloc_size, size + 1);
list->data[size++] = value;
value = DUMMY_VALUE;

bool got_whitespace = is_hipack_whitespace (p->look);
skipwhite (p, CHECK_OK);
Expand All @@ -394,65 +391,65 @@ parse_list (P, S)
}

matchchar (p, ']', "unterminated list value", CHECK_OK);
return (hipack_value_t) {
.type = HIPACK_LIST,
.v_list = list ? list : hipack_list_new (0),
};
result->type = HIPACK_LIST;
result->v_list = list;
return;

error:
hipack_value_free (&value);
hipack_list_free (list);
return DUMMY_VALUE;
return;
}


static hipack_value_t
parse_dict (P, S)
static void
parse_dict (P, hipack_value_t *result, S)
{
hipack_dict_t *dict = hipack_dict_new ();
matchchar (p, '{', NULL, CHECK_OK);
skipwhite (p, CHECK_OK);
parse_keyval_items (p, dict, '}', CHECK_OK);
matchchar (p, '}', "unterminated dict value", CHECK_OK);
return (hipack_value_t) { .type = HIPACK_DICT, .v_dict = dict };
result->type = HIPACK_DICT;
result->v_dict = dict;
return;

error:
hipack_dict_free (dict);
return DUMMY_VALUE;
return;
}


static hipack_value_t
parse_bool (P, S)
static void
parse_bool (P, hipack_value_t *result, S)
{
result->type = HIPACK_BOOL;
if (p->look == 'T' || p->look == 't') {
nextchar (p, CHECK_OK);
matchchar (p, 'r', NULL, CHECK_OK);
matchchar (p, 'u', NULL, CHECK_OK);
matchchar (p, 'e', NULL, CHECK_OK);
return (hipack_value_t) { .type = HIPACK_BOOL, .v_bool = true };
result->v_bool = true;
} else if (p->look == 'F' || p->look == 'f') {
nextchar (p, CHECK_OK);
matchchar (p, 'a', NULL, CHECK_OK);
matchchar (p, 'l', NULL, CHECK_OK);
matchchar (p, 's', NULL, CHECK_OK);
matchchar (p, 'e', NULL, CHECK_OK);
return (hipack_value_t) { .type = HIPACK_BOOL, .v_bool = false };
result->v_bool = false;
}
return;

error:
p->error = "invalid boolean value";
return DUMMY_VALUE;
}


static hipack_value_t
parse_number (P, S)
static void
parse_number (P, hipack_value_t *result, S)
{
hipack_string_t *hstr = NULL;
uint32_t alloc_size = 0;
uint32_t size = 0;
hipack_value_t result;

#define SAVE_LOOK( ) \
hstr = string_resize (hstr, &alloc_size, size + 1); \
Expand Down Expand Up @@ -534,8 +531,8 @@ parse_number (P, S)
char *endptr = NULL;
long v = strtol ((const char*) hstr->data, &endptr, 16);
/* TODO: Check for overflow. */
result.type = HIPACK_INTEGER;
result.v_integer = (int32_t) v;
result->type = HIPACK_INTEGER;
result->v_integer = (int32_t) v;
} else if (is_octal) {
assert (!is_hex);
if (exp_seen || dot_seen) {
Expand All @@ -544,22 +541,22 @@ parse_number (P, S)
}
long v = strtol ((const char*) hstr->data, &endptr, 8);
/* TODO: Check for overflow. */
result.type = HIPACK_INTEGER;
result.v_integer = (int32_t) v;
result->type = HIPACK_INTEGER;
result->v_integer = (int32_t) v;
} else if (dot_seen || exp_seen) {
assert (!is_hex);
assert (!is_octal);
result.type = HIPACK_FLOAT;
result.v_float = strtod ((const char*) hstr->data, &endptr);
result->type = HIPACK_FLOAT;
result->v_float = strtod ((const char*) hstr->data, &endptr);
} else {
assert (!is_hex);
assert (!is_octal);
assert (!exp_seen);
assert (!dot_seen);
long v = strtol ((const char*) hstr->data, &endptr, 10);
/* TODO: Check for overflow. */
result.type = HIPACK_INTEGER;
result.v_integer = (int32_t) v;
result->type = HIPACK_INTEGER;
result->v_integer = (int32_t) v;
}

if (endptr && *endptr != '\0') {
Expand All @@ -568,12 +565,43 @@ parse_number (P, S)
}

hipack_string_free (hstr);
return result;
return;

error:
p->error = "invalid numeric value";
hipack_string_free (hstr);
return DUMMY_VALUE;
}


static void
parse_annotations (P, hipack_value_t *result, S)
{
hipack_string_t *key = NULL;
while (p->look == ':') {
p->look = nextchar_raw (p, CHECK_OK);
key = parse_key (p, CHECK_OK);
skipwhite (p, CHECK_OK);

/* Check if the annotation is already in the set. */
if (result->annot && hipack_dict_get (result->annot, key)) {
p->error = "duplicate annotation";
*status = kStatusError;
goto error;
}
/* Add the annotation to the set. */
if (!result->annot)
result->annot = hipack_dict_new ();

static const hipack_value_t annot_present = {
.type = HIPACK_BOOL,
.v_bool = true,
};
hipack_dict_set_adopt_key (result->annot, &key, &annot_present);
}

error:
if (key)
hipack_string_free (key);
}


Expand All @@ -582,33 +610,36 @@ parse_value (P, S)
{
hipack_value_t result = DUMMY_VALUE;

parse_annotations (p, &result, CHECK_OK);

switch (p->look) {
case '"': /* String */
result = parse_string (p, CHECK_OK);
parse_string (p, &result, CHECK_OK);
break;

case '[': /* List */
result = parse_list (p, CHECK_OK);
parse_list (p, &result, CHECK_OK);
break;

case '{': /* Dict */
result = parse_dict (p, CHECK_OK);
parse_dict (p, &result, CHECK_OK);
break;

case 'T': /* Bool */
case 't':
case 'F':
case 'f':
result = parse_bool (p, CHECK_OK);
parse_bool (p, &result, CHECK_OK);
break;

default: /* Integer or Float */
result = parse_number (p, CHECK_OK);
parse_number (p, &result, CHECK_OK);
break;
}

error:
return result;
hipack_value_free (&result);
return DUMMY_VALUE;
}


Expand All @@ -631,8 +662,7 @@ parse_keyval_items (P, hipack_dict_t *result, int eos, S)
if (is_hipack_whitespace (p->look)) {
got_separator = true;
skipwhite (p, CHECK_OK);
}
switch (p->look) {
} else switch (p->look) {
case ':':
nextchar (p, CHECK_OK);
skipwhite (p, CHECK_OK);
Expand Down
53 changes: 52 additions & 1 deletion hipack.h
Expand Up @@ -61,7 +61,8 @@ typedef struct hipack_list hipack_list_t;


struct hipack_value {
hipack_type_t type;
hipack_type_t type;
hipack_dict_t *annot;
union {
int32_t v_integer;
double v_float;
Expand Down Expand Up @@ -141,6 +142,9 @@ extern void hipack_dict_set (hipack_dict_t *dict,
const hipack_string_t *key,
const hipack_value_t *value);

extern void hipack_dict_del (hipack_dict_t *dict,
const hipack_string_t *key);

extern hipack_value_t* hipack_dict_get (const hipack_dict_t *dict,
const hipack_string_t *key);

Expand Down Expand Up @@ -197,6 +201,9 @@ hipack_value_free (hipack_value_t *value)
{
assert (value);

if (value->annot)
hipack_dict_free (value->annot);

switch (value->type) {
case HIPACK_INTEGER:
case HIPACK_FLOAT:
Expand All @@ -219,6 +226,50 @@ hipack_value_free (hipack_value_t *value)
}


static inline void
hipack_value_add_annot (hipack_value_t *value,
const char *annot)
{
assert (value);
assert (annot);

if (!value->annot) {
value->annot = hipack_dict_new ();
}

static const hipack_value_t bool_true = {
.type = HIPACK_BOOL,
.v_bool = true,
};
hipack_string_t *key = hipack_string_new_from_string (annot);
hipack_dict_set_adopt_key (value->annot, &key, &bool_true);
}

static inline bool
hipack_value_has_annot (const hipack_value_t *value,
const char *annot)
{
assert (value);
assert (annot);

/* TODO: Try to avoid the string copy. */
hipack_string_t *key = hipack_string_new_from_string (annot);
bool result = (value->annot) && hipack_dict_get (value->annot, key);
hipack_string_free (key);
return result;
}

static inline void
hipack_value_del_annot (hipack_value_t *value,
const char *annot)
{
assert (value);
assert (annot);

hipack_string_t *key = hipack_string_new_from_string (annot);
}


typedef struct {
int (*getchar) (void*);
void *getchar_data;
Expand Down

0 comments on commit b6f267d

Please sign in to comment.