[util] Add a PL dictionary to hash table parser
This commit is contained in:
parent
55c1ed124d
commit
fefb32bf13
|
@ -147,7 +147,7 @@ int PL_Line (const plitem_t *item) __attribute__((pure));
|
||||||
\return the size in bytes of the binary object 0 if binary isn't a binary
|
\return the size in bytes of the binary object 0 if binary isn't a binary
|
||||||
object.
|
object.
|
||||||
*/
|
*/
|
||||||
size_t PL_BinarySize (const plitem_t *item) __attribute__((pure));
|
size_t PL_BinarySize (const plitem_t *binary) __attribute__((pure));
|
||||||
|
|
||||||
/** Retrieve the data from a binary object.
|
/** Retrieve the data from a binary object.
|
||||||
|
|
||||||
|
@ -323,9 +323,9 @@ void PL_Free (plitem_t *item);
|
||||||
|
|
||||||
Can be used recursively to parse deep hierarchies.
|
Can be used recursively to parse deep hierarchies.
|
||||||
|
|
||||||
\param dict The dictionary object to parse
|
|
||||||
\param fields Array of field items describing the structure. Terminated
|
\param fields Array of field items describing the structure. Terminated
|
||||||
by a field item with a null \a name pointer.
|
by a field item with a null \a name pointer.
|
||||||
|
\param dict The dictionary object to parse
|
||||||
\param data Pointer to the structure into which the data will be
|
\param data Pointer to the structure into which the data will be
|
||||||
parsed.
|
parsed.
|
||||||
\param messages Array object supplied by the caller used for storing
|
\param messages Array object supplied by the caller used for storing
|
||||||
|
@ -344,8 +344,90 @@ void PL_Free (plitem_t *item);
|
||||||
*/
|
*/
|
||||||
int PL_ParseStruct (const plfield_t *fields, const plitem_t *dict,
|
int PL_ParseStruct (const plfield_t *fields, const plitem_t *dict,
|
||||||
void *data, plitem_t *messages, void *context);
|
void *data, plitem_t *messages, void *context);
|
||||||
int PL_ParseArray (const plfield_t *fields, const plitem_t *dict,
|
|
||||||
|
/** Parse an array object into a dynamic arrah (see darray.h).
|
||||||
|
|
||||||
|
For each object in the array, the field item is used to determine how to
|
||||||
|
parse the object. If the array is empty, the destination will be
|
||||||
|
initialized to an empty array.
|
||||||
|
|
||||||
|
When an error occurs (incorrect item type (item type does not match the
|
||||||
|
type specified in the element object) or the element object's \a parser
|
||||||
|
returns 0), processing continues but the error result is returned.
|
||||||
|
|
||||||
|
Can be used recursively to parse deep hierarchies.
|
||||||
|
|
||||||
|
\param field Pointer to a single field that has the field data pointer
|
||||||
|
set to reference a plelement_t object used to describe
|
||||||
|
the contents of the array.
|
||||||
|
\param array The array object to parse
|
||||||
|
\param data Pointer to the pointer to which the dynamic array will
|
||||||
|
be written. The dynamic array is allocated using
|
||||||
|
DARRAY_ALLOCFIXED().
|
||||||
|
\param messages Array object supplied by the caller used for storing
|
||||||
|
messages. The messages may or may not indicate errors (its
|
||||||
|
contents are not checked). This function itself will add
|
||||||
|
only string objects.
|
||||||
|
If there are any errors, suitable messages will be found in
|
||||||
|
the \a messages object. However, just because there are no
|
||||||
|
errors doesn't mean that \a messages will remain empty as
|
||||||
|
a field's \a parser may add other messages. The standard
|
||||||
|
message format is "[line number]: [message]". If the line
|
||||||
|
number is 0, then the actual line is unknown (due to the
|
||||||
|
source item not being parsed from a file or string).
|
||||||
|
\param context Additional context data passed to the parser.
|
||||||
|
\return 0 if there are any errors, 1 if there are no errors.
|
||||||
|
*/
|
||||||
|
int PL_ParseArray (const plfield_t *field, const plitem_t *array,
|
||||||
void *data, plitem_t *messages, void *context);
|
void *data, plitem_t *messages, void *context);
|
||||||
|
|
||||||
|
/** Parse a dictionary object into a hash table.
|
||||||
|
|
||||||
|
For each key in the dictionary, the element object is used to determine
|
||||||
|
how to parse the object associated with that key. Duplicate keys are an
|
||||||
|
error: they must be unique. A suitable message is added to the
|
||||||
|
\a messages object. If the dictionary object is empty, the destination
|
||||||
|
table is left unmodified.
|
||||||
|
|
||||||
|
When an error occurs (duplicate keys, incorrect type, or the element
|
||||||
|
object's \a parser returns 0), processing continues but the error
|
||||||
|
result is returned.
|
||||||
|
|
||||||
|
Can be used recursively to parse deep hierarchies.
|
||||||
|
|
||||||
|
Hash_Add() is used to add objects to the hash table, and Hash_Find() is
|
||||||
|
used to check for duplicates. Hash_Free() is used to free unused
|
||||||
|
objects. The means that the hash table is expected to use standard
|
||||||
|
objects with embedded keys (the parser is expected to put the key in the
|
||||||
|
object) and to have a free function.
|
||||||
|
|
||||||
|
The parser's data paramenter points to a pre-allocated block of memory
|
||||||
|
of the sized indicated by the element object's size field, using the
|
||||||
|
element object's alloc callback. The name field in the field paramenter
|
||||||
|
is set to the object's key and remains owned by the dictionary.
|
||||||
|
|
||||||
|
\param field Pointer to a single field that has the field data pointer
|
||||||
|
set to reference a plelement_t object used to describe
|
||||||
|
the contents of the dictionary.
|
||||||
|
\param dict The dictionary object to parse
|
||||||
|
\param data Pointer to the structure into which the data will be
|
||||||
|
parsed.
|
||||||
|
\param messages Array object supplied by the caller used for storing
|
||||||
|
messages. The messages may or may not indicate errors (its
|
||||||
|
contents are not checked). This function itself will add
|
||||||
|
only string objects.
|
||||||
|
If there are any errors, suitable messages will be found in
|
||||||
|
the \a messages object. However, just because there are no
|
||||||
|
errors doesn't mean that \a messages will remain empty as
|
||||||
|
a field's \a parser may add other messages. The standard
|
||||||
|
message format is "[line number]: [message]". If the line
|
||||||
|
number is 0, then the actual line is unknown (due to the
|
||||||
|
source item not being parsed from a file or string).
|
||||||
|
\param context Additional context data passed to the parser.
|
||||||
|
\return 0 if there are any errors, 1 if there are no errors.
|
||||||
|
*/
|
||||||
|
int PL_ParseSymtab (const plfield_t *field, const plitem_t *dict,
|
||||||
|
void *data, plitem_t *messages, void *context);
|
||||||
void __attribute__((format(printf,3,4)))
|
void __attribute__((format(printf,3,4)))
|
||||||
PL_Message (plitem_t *messages, const plitem_t *item, const char *fmt, ...);
|
PL_Message (plitem_t *messages, const plitem_t *item, const char *fmt, ...);
|
||||||
|
|
||||||
|
|
|
@ -1247,3 +1247,65 @@ PL_ParseArray (const plfield_t *field, const plitem_t *array, void *data,
|
||||||
*(arr_t **) data = arr;
|
*(arr_t **) data = arr;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VISIBLE int
|
||||||
|
PL_ParseSymtab (const plfield_t *field, const plitem_t *dict, void *data,
|
||||||
|
plitem_t *messages, void *context)
|
||||||
|
{
|
||||||
|
void **list, **l;
|
||||||
|
dictkey_t *current;
|
||||||
|
int result = 1;
|
||||||
|
plparser_t parser;
|
||||||
|
__auto_type tab = (hashtab_t *) data;
|
||||||
|
|
||||||
|
plelement_t *element = (plelement_t *) field->data;
|
||||||
|
plfield_t f = { 0, 0, element->type, element->parser, element->data };
|
||||||
|
|
||||||
|
if (dict->type != QFDictionary) {
|
||||||
|
PL_Message (messages, dict, "error: not a dictionary object");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f.parser) {
|
||||||
|
parser = f.parser;
|
||||||
|
} else {
|
||||||
|
PL_Message (messages, dict, "no parser set");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(l = list = Hash_GetList ((hashtab_t *) dict->data))) {
|
||||||
|
// empty struct: leave as default
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *obj = element->alloc (element->stride);
|
||||||
|
while ((current = (dictkey_t *) *l++)) {
|
||||||
|
const char *key = current->key;
|
||||||
|
plitem_t *item = current->value;
|
||||||
|
|
||||||
|
if (item->type != element->type) {
|
||||||
|
PL_Message (messages, item,
|
||||||
|
"error: element %s is the wrong type"
|
||||||
|
" Got %s, expected %s", key,
|
||||||
|
pl_types[element->type],
|
||||||
|
pl_types[item->type]);
|
||||||
|
result = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
f.name = key;
|
||||||
|
if (Hash_Find (tab, key)) {
|
||||||
|
PL_Message (messages, item, "duplicate name");
|
||||||
|
result = 0;
|
||||||
|
} else {
|
||||||
|
if (!parser (&f, item, obj, messages, context)) {
|
||||||
|
result = 0;
|
||||||
|
} else {
|
||||||
|
Hash_Add (tab, obj);
|
||||||
|
obj = element->alloc (element->stride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Hash_Free (tab, obj);
|
||||||
|
free (list);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue