[util] Add plist parser for dictionary -> array

PL_ParseLabeledArray works the same way as PL_ParseArray, but instead
takes a dictionary object. The keys of the items are ignored, and the
order is not preserved (at this stage), but this is a cleaner solution
to getting an array of objects when the definitions of those objects
need to be accessible by name as well.
This commit is contained in:
Bill Currie 2021-02-09 15:24:28 +09:00
parent bc763da9f6
commit 0dcd946063
2 changed files with 98 additions and 4 deletions

View file

@ -396,6 +396,45 @@ int PL_ParseStruct (const plfield_t *fields, const plitem_t *dict,
int PL_ParseArray (const plfield_t *field, const plitem_t *array,
void *data, plitem_t *messages, void *context);
/** Parse a dictionary object into a dynamic array (see darray.h).
This is useful when the dictionary object is meant to be a labeled list
rather than a representation of a structure.
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 dict The dict 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_OBJ().
\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 and allocator.
\return 0 if there are any errors, 1 if there are no errors.
*/
int PL_ParseLabeledArray (const plfield_t *field, const plitem_t *dict,
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

View file

@ -1211,10 +1211,7 @@ PL_ParseStruct (const plfield_t *fields, const plitem_t *dict, void *data,
return 0;
}
if (!(l = list = Hash_GetList ((hashtab_t *) dict->data))) {
// empty struct: leave as default
return 1;
}
l = list = Hash_GetList ((hashtab_t *) dict->data);
while ((current = (dictkey_t *) *l++)) {
const plfield_t *f;
@ -1299,6 +1296,64 @@ PL_ParseArray (const plfield_t *field, const plitem_t *array, void *data,
return result;
}
VISIBLE int
PL_ParseLabeledArray (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;
plelement_t *element = (plelement_t *) field->data;
typedef struct arr_s DARRAY_TYPE(byte) arr_t;
arr_t *arr;
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 {
parser = pl_default_parser;
}
list = Hash_GetList ((hashtab_t *) dict->data);
int numvals = 0;
for (l = list; *l; l++) {
numvals++;
}
arr = DARRAY_ALLOCFIXED_OBJ (arr_t, numvals * element->stride,
element->alloc, context);
memset (arr->a, 0, arr->size);
// the array is allocated using bytes, but need the actual number of
// elements in the array
arr->size = arr->maxSize = numvals;
for (int i = 0; i < numvals; i++) {
current = list[i];
plitem_t *item = current->value;
void *eledata = &arr->a[i * element->stride];
if (!PL_CheckType (element->type, item->type)) {
char index[16];
snprintf (index, sizeof(index) - 1, "%d", i);
index[15] = 0;
PL_TypeMismatch (messages, item, index, element->type, item->type);
result = 0;
} else {
if (!parser (&f, item, eledata, messages, context)) {
result = 0;
}
}
}
*(arr_t **) data = arr;
return result;
}
VISIBLE int
PL_ParseSymtab (const plfield_t *field, const plitem_t *dict, void *data,
plitem_t *messages, void *context)