mirror of
https://git.telodendria.io/Telodendria/Cytoplasm.git
synced 2024-10-30 09:10:47 +03:00
29070c8f41
Closes #17.
1140 lines
44 KiB
C
1140 lines
44 KiB
C
/*
|
|
* Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person
|
|
* obtaining a copy of this software and associated documentation files
|
|
* (the "Software"), to deal in the Software without restriction,
|
|
* including without limitation the rights to use, copy, modify, merge,
|
|
* publish, distribute, sublicense, and/or sell copies of the Software,
|
|
* and to permit persons to whom the Software is furnished to do so,
|
|
* subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
#include <Log.h>
|
|
#include <Array.h>
|
|
#include <Graph.h>
|
|
#include <HashMap.h>
|
|
#include <Args.h>
|
|
#include <Json.h>
|
|
#include <Str.h>
|
|
#include <Memory.h>
|
|
|
|
#define MAX_DEPENDENCIES 32
|
|
|
|
static char *
|
|
Trim(char c, char *str)
|
|
{
|
|
while (*str == c)
|
|
str++;
|
|
return str;
|
|
}
|
|
|
|
static JsonType
|
|
TypeToJsonType(char *type)
|
|
{
|
|
if (StrEquals(type, "object"))
|
|
{
|
|
return JSON_OBJECT;
|
|
}
|
|
else if (StrEquals(type, "array"))
|
|
{
|
|
return JSON_ARRAY;
|
|
}
|
|
else if (StrEquals(type, "string"))
|
|
{
|
|
return JSON_STRING;
|
|
}
|
|
else if (StrEquals(type, "integer"))
|
|
{
|
|
return JSON_INTEGER;
|
|
}
|
|
else if (StrEquals(type, "float"))
|
|
{
|
|
return JSON_FLOAT;
|
|
}
|
|
else if (StrEquals(type, "boolean"))
|
|
{
|
|
return JSON_BOOLEAN;
|
|
}
|
|
else
|
|
{
|
|
if (*type == '[' && type[strlen(type) - 1] == ']')
|
|
{
|
|
return JSON_ARRAY;
|
|
}
|
|
else
|
|
{
|
|
return JSON_OBJECT;
|
|
}
|
|
}
|
|
}
|
|
|
|
static char *
|
|
JsonTypeToStr(JsonType type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case JSON_OBJECT:
|
|
return "JSON_OBJECT";
|
|
case JSON_ARRAY:
|
|
return "JSON_ARRAY";
|
|
case JSON_STRING:
|
|
return "JSON_STRING";
|
|
case JSON_INTEGER:
|
|
return "JSON_INTEGER";
|
|
case JSON_FLOAT:
|
|
return "JSON_FLOAT";
|
|
case JSON_BOOLEAN:
|
|
return "JSON_BOOLEAN";
|
|
case JSON_NULL:
|
|
default:
|
|
return "JSON_NULL";
|
|
}
|
|
}
|
|
|
|
int
|
|
Main(Array * args)
|
|
{
|
|
ArgParseState arg;
|
|
int opt;
|
|
int ret = EXIT_FAILURE;
|
|
|
|
char *schema = NULL;
|
|
char *header = NULL;
|
|
char *impl = NULL;
|
|
|
|
Stream *schemaFile = NULL;
|
|
Stream *headerFile = NULL;
|
|
Stream *implFile = NULL;
|
|
|
|
HashMap *schemaJson = NULL;
|
|
|
|
JsonValue *val;
|
|
char *type;
|
|
JsonValue *typeVal;
|
|
|
|
HashMap *types;
|
|
|
|
size_t i;
|
|
|
|
Graph *dependencyGraph = GraphCreate(MAX_DEPENDENCIES);
|
|
HashMap *typeToNode = HashMapCreate();
|
|
Array *requiredTypes = ArrayCreate();
|
|
|
|
Node *sortedNodes;
|
|
size_t sortedNodesLen;
|
|
Node *node;
|
|
|
|
char *guard;
|
|
char *headerName;
|
|
|
|
if (!requiredTypes)
|
|
{
|
|
Log(LOG_ERR, "Unable to allocate memory for type name storage");
|
|
goto finish;
|
|
}
|
|
|
|
ArgParseStateInit(&arg);
|
|
while ((opt = ArgParse(&arg, args, "s:h:c:")) != -1)
|
|
{
|
|
switch (opt)
|
|
{
|
|
case 's':
|
|
schema = arg.optArg;
|
|
break;
|
|
case 'h':
|
|
header = arg.optArg;
|
|
break;
|
|
case 'c':
|
|
impl = arg.optArg;
|
|
break;
|
|
default:
|
|
goto finish;
|
|
}
|
|
}
|
|
|
|
if (!schema)
|
|
{
|
|
Log(LOG_ERR, "Please specify a schema with -s");
|
|
goto finish;
|
|
}
|
|
|
|
if (!header)
|
|
{
|
|
Log(LOG_ERR, "Please specify an output header with -h");
|
|
goto finish;
|
|
}
|
|
|
|
if (!impl)
|
|
{
|
|
Log(LOG_ERR, "Please specify an output C source file with -c");
|
|
goto finish;
|
|
}
|
|
|
|
schemaFile = StreamOpen(schema, "r");
|
|
if (!schemaFile)
|
|
{
|
|
Log(LOG_ERR, "Unable to open '%s' for reading.", schema);
|
|
goto finish;
|
|
}
|
|
|
|
headerFile = StreamOpen(header, "w");
|
|
if (!headerFile)
|
|
{
|
|
Log(LOG_ERR, "Unable to open '%s' for writing.", header);
|
|
goto finish;
|
|
}
|
|
|
|
implFile = StreamOpen(impl, "w");
|
|
if (!implFile)
|
|
{
|
|
Log(LOG_ERR, "Unable to open '%s' for writing.", impl);
|
|
goto finish;
|
|
}
|
|
|
|
schemaJson = JsonDecode(schemaFile);
|
|
if (!schemaJson)
|
|
{
|
|
Log(LOG_ERR, "JSON syntax error.");
|
|
goto finish;
|
|
}
|
|
|
|
StreamClose(schemaFile);
|
|
schemaFile = NULL;
|
|
|
|
val = HashMapGet(schemaJson, "guard");
|
|
|
|
if (!val)
|
|
{
|
|
Log(LOG_WARNING, "Guard not specified, using 'J2S_SCHEMA_H', which may not be unique.");
|
|
HashMapSet(schemaJson, "guard", JsonValueString("J2S_SCHEMA_H"));
|
|
}
|
|
else if (JsonValueType(val) != JSON_STRING)
|
|
{
|
|
Log(LOG_ERR, "Validation error: 'guard' must be a string.");
|
|
goto finish;
|
|
}
|
|
|
|
val = HashMapGet(schemaJson, "header");
|
|
if (!val)
|
|
{
|
|
Log(LOG_WARNING, "Header name not specified, using 'Schema.h', which is probably wrong.");
|
|
HashMapSet(schemaJson, "header", JsonValueString("Schema.h"));
|
|
}
|
|
else if (JsonValueType(val) != JSON_STRING)
|
|
{
|
|
Log(LOG_ERR, "Validation error: 'header' must be a string.");
|
|
goto finish;
|
|
}
|
|
|
|
val = HashMapGet(schemaJson, "includes");
|
|
|
|
if (val && JsonValueType(val) != JSON_ARRAY)
|
|
{
|
|
Log(LOG_ERR, "Validation error: 'includes' must be an array.");
|
|
goto finish;
|
|
}
|
|
|
|
for (i = 0; i < ArraySize(JsonValueAsArray(val)); i++)
|
|
{
|
|
if (JsonValueType(ArrayGet(JsonValueAsArray(val), 0)) != JSON_STRING)
|
|
{
|
|
Log(LOG_ERR, "Validation error: 'includes[%lu]' is not a string.", i);
|
|
goto finish;
|
|
}
|
|
}
|
|
|
|
val = HashMapGet(schemaJson, "types");
|
|
if (JsonValueType(val) != JSON_OBJECT)
|
|
{
|
|
Log(LOG_ERR, "Validation error: 'types' must be an object.");
|
|
goto finish;
|
|
}
|
|
|
|
types = JsonValueAsObject(val);
|
|
|
|
while (HashMapIterate(types, &type, (void **) &typeVal))
|
|
{
|
|
HashMap *typeObj;
|
|
JsonValue *typeTypeVal;
|
|
char *typeType;
|
|
|
|
JsonValue *typeFieldsVal;
|
|
HashMap *typeFields;
|
|
|
|
char *fieldName;
|
|
JsonValue *fieldVal;
|
|
HashMap *fieldObj;
|
|
|
|
Node *typeNode;
|
|
|
|
if (JsonValueType(typeVal) != JSON_OBJECT)
|
|
{
|
|
Log(LOG_ERR, "Validation error: 'types.%s' must be an object.", type);
|
|
goto finish;
|
|
}
|
|
|
|
typeObj = JsonValueAsObject(typeVal);
|
|
typeTypeVal = HashMapGet(typeObj, "type");
|
|
if (JsonValueType(typeTypeVal) != JSON_STRING)
|
|
{
|
|
Log(LOG_ERR, "Validation error: 'types.%s.type' must be a string.", type);
|
|
goto finish;
|
|
}
|
|
|
|
typeType = JsonValueAsString(typeTypeVal);
|
|
|
|
typeNode = HashMapGet(typeToNode, type);
|
|
if (!typeNode)
|
|
{
|
|
typeNode = Malloc(sizeof(Node));
|
|
*typeNode = ArraySize(requiredTypes);
|
|
HashMapSet(typeToNode, type, typeNode);
|
|
ArrayAdd(requiredTypes, StrDuplicate(type));
|
|
}
|
|
|
|
if (StrEquals(typeType, "struct"))
|
|
{
|
|
typeFieldsVal = HashMapGet(typeObj, "fields");
|
|
if (JsonValueType(typeFieldsVal) != JSON_OBJECT)
|
|
{
|
|
Log(LOG_ERR, "Validation error: 'types.%s.fields' must be an object.", type);
|
|
goto finish;
|
|
}
|
|
|
|
typeFields = JsonValueAsObject(typeFieldsVal);
|
|
|
|
while (HashMapIterate(typeFields, &fieldName, (void **) &fieldVal))
|
|
{
|
|
char *fieldType;
|
|
int isArrType = 0;
|
|
JsonValue *requiredVal;
|
|
JsonValue *ignoreVal;
|
|
|
|
if (JsonValueType(fieldVal) != JSON_OBJECT)
|
|
{
|
|
Log(LOG_ERR, "Validation error: 'types.%s.fields.%s' must be an object.", type, fieldName);
|
|
goto finish;
|
|
}
|
|
|
|
fieldObj = JsonValueAsObject(fieldVal);
|
|
|
|
fieldType = JsonValueAsString(HashMapGet(fieldObj, "type"));
|
|
if (!fieldType)
|
|
{
|
|
Log(LOG_ERR, "Validation error: 'types.%s.fields.%s.type' is required and must be a string.", type, fieldName);
|
|
goto finish;
|
|
}
|
|
|
|
if (*fieldType == '[' && fieldType[strlen(fieldType) - 1] == ']')
|
|
{
|
|
fieldType++;
|
|
fieldType[strlen(fieldType) - 1] = '\0';
|
|
isArrType = 1;
|
|
}
|
|
|
|
if (!StrEquals(fieldType, "object") &&
|
|
!StrEquals(fieldType, "array") &&
|
|
!StrEquals(fieldType, "string") &&
|
|
!StrEquals(fieldType, "integer") &&
|
|
!StrEquals(fieldType, "float") &&
|
|
!StrEquals(fieldType, "boolean"))
|
|
{
|
|
Node *node = HashMapGet(typeToNode, fieldType);
|
|
|
|
if (!node)
|
|
{
|
|
node = Malloc(sizeof(Node));
|
|
*node = ArraySize(requiredTypes);
|
|
HashMapSet(typeToNode, fieldType, node);
|
|
ArrayAdd(requiredTypes, StrDuplicate(fieldType));
|
|
}
|
|
|
|
GraphEdgeSet(dependencyGraph, *node, *typeNode, 1);
|
|
}
|
|
|
|
if (isArrType)
|
|
{
|
|
fieldType[strlen(fieldType)] = ']';
|
|
}
|
|
|
|
requiredVal = HashMapGet(fieldObj, "required");
|
|
if (requiredVal && JsonValueType(requiredVal) != JSON_BOOLEAN)
|
|
{
|
|
Log(LOG_ERR, "Validation error: 'types.%s.fields.%s.required' must be a boolean.", type, fieldName);
|
|
goto finish;
|
|
}
|
|
|
|
ignoreVal = HashMapGet(fieldObj, "ignore");
|
|
if (ignoreVal && JsonValueType(ignoreVal) != JSON_BOOLEAN)
|
|
{
|
|
Log(LOG_ERR, "Validation error: 'types.%s.fields.%s.ignore' must be a boolean.", type, fieldName);
|
|
goto finish;
|
|
}
|
|
}
|
|
}
|
|
else if (StrEquals(typeType, "enum"))
|
|
{
|
|
typeFieldsVal = HashMapGet(typeObj, "fields");
|
|
if (JsonValueType(typeFieldsVal) != JSON_OBJECT)
|
|
{
|
|
Log(LOG_ERR, "Validation error: 'types.%s.fields' must be an object.", type);
|
|
goto finish;
|
|
}
|
|
|
|
typeFields = JsonValueAsObject(typeFieldsVal);
|
|
|
|
while (HashMapIterate(typeFields, &fieldName, (void **) &fieldVal))
|
|
{
|
|
char *name;
|
|
|
|
if (JsonValueType(fieldVal) != JSON_OBJECT)
|
|
{
|
|
Log(LOG_ERR, "Validation error: 'types.%s.fields.%s' must be an object.", type, fieldName);
|
|
goto finish;
|
|
}
|
|
|
|
fieldObj = JsonValueAsObject(fieldVal);
|
|
|
|
name = JsonValueAsString(HashMapGet(fieldObj, "name"));
|
|
if (!name)
|
|
{
|
|
Log(LOG_ERR, "Validation error: 'types.%s.fields.%s.name' is required and must be a string.", type, fieldName);
|
|
goto finish;
|
|
}
|
|
}
|
|
}
|
|
else if (StrEquals(typeType, "extern"))
|
|
{
|
|
/*
|
|
* No code will be generated for this type. We simply assume that it exists.
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
Log(LOG_ERR, "Validation error: 'types.%s.type' must be 'struct' or 'enum'.", type);
|
|
goto finish;
|
|
}
|
|
}
|
|
|
|
sortedNodes = GraphTopologicalSort(dependencyGraph, &sortedNodesLen);
|
|
|
|
GraphFree(dependencyGraph);
|
|
|
|
for (i = 0; i < sortedNodesLen; i++)
|
|
{
|
|
char *type = ArrayGet(requiredTypes, sortedNodes[i]);
|
|
|
|
if (!type)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!HashMapGet(types, type))
|
|
{
|
|
Log(LOG_ERR, "No type definition for required type '%s'", type);
|
|
goto finish;
|
|
}
|
|
}
|
|
|
|
guard = JsonValueAsString(HashMapGet(schemaJson, "guard"));
|
|
|
|
StreamPrintf(headerFile, "/* Generated by j2s */\n\n");
|
|
StreamPrintf(headerFile, "#ifndef %s\n", guard);
|
|
StreamPrintf(headerFile, "#define %s\n\n", guard);
|
|
|
|
StreamPrintf(headerFile, "#include <Cytoplasm/Array.h>\n");
|
|
StreamPrintf(headerFile, "#include <Cytoplasm/HashMap.h>\n");
|
|
StreamPrintf(headerFile, "#include <Cytoplasm/Int64.h>\n");
|
|
|
|
StreamPutc(headerFile, '\n');
|
|
|
|
for (i = 0; i < ArraySize(JsonValueAsArray(HashMapGet(schemaJson, "include"))); i++)
|
|
{
|
|
char *h = JsonValueAsString(ArrayGet(JsonValueAsArray(HashMapGet(schemaJson, "include")), i));
|
|
|
|
StreamPrintf(headerFile, "#include <%s>\n", h);
|
|
}
|
|
|
|
StreamPutc(headerFile, '\n');
|
|
|
|
for (i = 0; i < sortedNodesLen; i++)
|
|
{
|
|
char *type = ArrayGet(requiredTypes, sortedNodes[i]);
|
|
char *typeType;
|
|
HashMap *fields;
|
|
|
|
char *field;
|
|
HashMap *fieldDesc;
|
|
|
|
if (!type)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
typeType = JsonValueAsString(JsonGet(types, 2, type, "type"));
|
|
|
|
if (StrEquals(typeType, "extern"))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
fields = JsonValueAsObject(JsonGet(types, 2, type, "fields"));
|
|
|
|
StreamPrintf(headerFile, "typedef %s %s\n{\n", typeType, type);
|
|
|
|
if (StrEquals(typeType, "struct"))
|
|
{
|
|
while (HashMapIterate(fields, &field, (void **) &fieldDesc))
|
|
{
|
|
char *fieldType;
|
|
char *cType;
|
|
|
|
fieldDesc = JsonValueAsObject((JsonValue *) fieldDesc);
|
|
|
|
fieldType = JsonValueAsString(HashMapGet(fieldDesc, "type"));
|
|
|
|
if (StrEquals(fieldType, "string"))
|
|
{
|
|
cType = "char *";
|
|
}
|
|
else if (StrEquals(fieldType, "integer"))
|
|
{
|
|
cType = "Int64";
|
|
}
|
|
else if (StrEquals(fieldType, "boolean"))
|
|
{
|
|
cType = "int";
|
|
}
|
|
else if (StrEquals(fieldType, "float"))
|
|
{
|
|
cType = "double";
|
|
}
|
|
else if (StrEquals(fieldType, "object"))
|
|
{
|
|
cType = "HashMap *";
|
|
}
|
|
else if (StrEquals(fieldType, "array") || (*fieldType == '[' && fieldType[strlen(fieldType) - 1] == ']'))
|
|
{
|
|
cType = "Array *";
|
|
}
|
|
else
|
|
{
|
|
cType = fieldType;
|
|
}
|
|
|
|
StreamPrintf(headerFile, " %s %s;\n", cType, field);
|
|
}
|
|
|
|
StreamPrintf(headerFile, "} %s;\n\n", type);
|
|
}
|
|
else if (StrEquals(typeType, "enum"))
|
|
{
|
|
/*
|
|
* Enums must be copied to an array because we have to know
|
|
* where we're at to know whether or not we need the trailing
|
|
* comma.
|
|
*/
|
|
Array *keys = HashMapKeys(fields);
|
|
|
|
size_t j;
|
|
|
|
for (j = 0; j < ArraySize(keys); j++)
|
|
{
|
|
char *key = ArrayGet(keys, j);
|
|
char *name;
|
|
char *value;
|
|
|
|
fieldDesc = JsonValueAsObject(HashMapGet(fields, key));
|
|
|
|
name = JsonValueAsString(HashMapGet(fieldDesc, "name"));
|
|
value = JsonValueAsString(HashMapGet(fieldDesc, "value"));
|
|
|
|
if (value)
|
|
{
|
|
StreamPrintf(headerFile, " %s = %s", name, value);
|
|
}
|
|
else
|
|
{
|
|
StreamPrintf(headerFile, " %s", name);
|
|
}
|
|
|
|
if (j < ArraySize(keys) - 1)
|
|
{
|
|
StreamPutc(headerFile, ',');
|
|
}
|
|
|
|
StreamPutc(headerFile, '\n');
|
|
}
|
|
|
|
ArrayFree(keys);
|
|
|
|
StreamPrintf(headerFile, "} %s;\n\n", type);
|
|
}
|
|
}
|
|
|
|
Free(sortedNodes);
|
|
for (i = 0; i < ArraySize(requiredTypes); i++)
|
|
{
|
|
Free(ArrayGet(requiredTypes, i));
|
|
}
|
|
ArrayFree(requiredTypes);
|
|
|
|
while (HashMapIterate(typeToNode, &type, (void **) &node))
|
|
{
|
|
Free(node);
|
|
}
|
|
HashMapFree(typeToNode);
|
|
|
|
headerName = JsonValueAsString(HashMapGet(schemaJson, "header"));
|
|
|
|
StreamPrintf(implFile, "/* Generated by j2s */\n\n");
|
|
StreamPrintf(implFile, "#include <%s>\n\n", headerName);
|
|
|
|
StreamPrintf(implFile, "#include <Cytoplasm/Memory.h>\n");
|
|
StreamPrintf(implFile, "#include <Cytoplasm/Json.h>\n");
|
|
StreamPrintf(implFile, "#include <Cytoplasm/Str.h>\n");
|
|
|
|
StreamPutc(implFile, '\n');
|
|
|
|
while (HashMapIterate(types, &type, (void **) &typeVal))
|
|
{
|
|
char *typeType = JsonValueAsString(JsonGet(types, 2, type, "type"));
|
|
HashMap *fields = JsonValueAsObject(JsonGet(types, 2, type, "fields"));
|
|
|
|
Array *keys = HashMapKeys(fields);
|
|
|
|
if (StrEquals(typeType, "struct"))
|
|
{
|
|
StreamPrintf(headerFile, "extern int %sFromJson(HashMap *, %s *, char **);\n", type, type);
|
|
StreamPrintf(implFile, "int\n%sFromJson(HashMap *json, %s *out, char **errp)\n{\n", type, type);
|
|
StreamPrintf(implFile, " JsonValue *val;\n");
|
|
StreamPrintf(implFile, " int enumParseRes;\n");
|
|
StreamPrintf(implFile, "\n");
|
|
StreamPrintf(implFile, " (void) enumParseRes;\n");
|
|
StreamPrintf(implFile, "\n");
|
|
StreamPrintf(implFile, " if (!json | !out)\n"
|
|
" {\n"
|
|
" *errp = \"Invalid pointers passed to %sFromJson()\";\n"
|
|
" return 0;\n"
|
|
" }\n\n"
|
|
,type);
|
|
for (i = 0; i < ArraySize(keys); i++)
|
|
{
|
|
char *key = ArrayGet(keys, i);
|
|
int required = JsonValueAsBoolean(JsonGet(fields, 2, key, "required"));
|
|
int ignore = JsonValueAsBoolean(JsonGet(fields, 2, key, "ignore"));
|
|
char *fieldType = JsonValueAsString(JsonGet(fields, 2, key, "type"));
|
|
int isEnum = StrEquals(JsonValueAsString(JsonGet(types, 2, fieldType, "type")), "enum");
|
|
JsonType jsonType = isEnum ? JSON_STRING : TypeToJsonType(fieldType);
|
|
char *jsonTypeStr = JsonTypeToStr(jsonType);
|
|
|
|
if (ignore)
|
|
{
|
|
StreamPrintf(implFile, " /* Ignored field: %s */\n\n", key);
|
|
continue;
|
|
}
|
|
|
|
StreamPrintf(implFile, " val = HashMapGet(json, \"%s\");\n", Trim('_', key));
|
|
|
|
StreamPrintf(implFile, " if (val)\n {\n");
|
|
StreamPrintf(implFile, " if (JsonValueType(val) != %s)\n {\n", jsonTypeStr);
|
|
StreamPrintf(implFile, " *errp = \"%s.%s must be of type %s.\";\n", type, Trim('_', key), fieldType);
|
|
StreamPrintf(implFile, " return 0;\n");
|
|
StreamPrintf(implFile, " }\n\n");
|
|
if (StrEquals(fieldType, "array"))
|
|
{
|
|
StreamPrintf(implFile, " val = JsonValueDuplicate(val);\n");
|
|
StreamPrintf(implFile, " out->%s = JsonValueAsArray(val);\n", key);
|
|
StreamPrintf(implFile, " Free(val); /* Not JsonValueFree() because we want the inner value. */\n");
|
|
}
|
|
else if (StrEquals(fieldType, "object"))
|
|
{
|
|
StreamPrintf(implFile, " val = JsonValueDuplicate(val);\n");
|
|
StreamPrintf(implFile, " out->%s = JsonValueAsObject(val);\n", key);
|
|
StreamPrintf(implFile, " Free(val); /* Not JsonValueFree() because we want the inner value. */\n");
|
|
}
|
|
else if (*fieldType == '[' && fieldType[strlen(fieldType) - 1] == ']')
|
|
{
|
|
fieldType++;
|
|
fieldType[strlen(fieldType) - 1] = '\0';
|
|
isEnum = StrEquals(JsonValueAsString(JsonGet(types, 2, fieldType, "type")), "enum");
|
|
jsonType = isEnum ? JSON_STRING : TypeToJsonType(fieldType);
|
|
|
|
StreamPrintf(implFile, " out->%s = ArrayCreate();\n", key);
|
|
StreamPrintf(implFile, " if (!out->%s)\n", key);
|
|
StreamPrintf(implFile, " {\n");
|
|
StreamPrintf(implFile, " *errp = \"Failed to allocate memory for %s.%s.\";\n", type, key);
|
|
StreamPrintf(implFile, " return 0;\n");
|
|
StreamPrintf(implFile, " }\n");
|
|
StreamPrintf(implFile, " else\n");
|
|
StreamPrintf(implFile, " {\n");
|
|
StreamPrintf(implFile, " Array *arr = JsonValueAsArray(val);\n");
|
|
StreamPrintf(implFile, " size_t i;\n");
|
|
StreamPrintf(implFile, "\n");
|
|
StreamPrintf(implFile, " for (i = 0; i <ArraySize(arr); i++)\n");
|
|
StreamPrintf(implFile, " {\n");
|
|
StreamPrintf(implFile, " JsonValue *v = ArrayGet(arr, i);\n");
|
|
|
|
if (StrEquals(fieldType, "integer") ||
|
|
StrEquals(fieldType, "float") ||
|
|
StrEquals(fieldType, "boolean"))
|
|
{
|
|
char *cType;
|
|
|
|
if (StrEquals(fieldType, "integer"))
|
|
{
|
|
cType = "Int64";
|
|
}
|
|
else if (StrEquals(fieldType, "float"))
|
|
{
|
|
cType = "double";
|
|
}
|
|
else if (StrEquals(fieldType, "boolean"))
|
|
{
|
|
cType = "int";
|
|
}
|
|
else
|
|
{
|
|
/* Should never happen */
|
|
cType = NULL;
|
|
}
|
|
|
|
*fieldType = toupper(*fieldType);
|
|
|
|
StreamPrintf(implFile, " %s *ref;\n", cType);
|
|
StreamPrintf(implFile, " if (JsonValueType(v) != %s)\n", JsonTypeToStr(jsonType));
|
|
StreamPrintf(implFile, " {\n");
|
|
StreamPrintf(implFile, " *errp = \"%s.%s[] contains an invalid value.\";\n", type, key);
|
|
StreamPrintf(implFile, " return 0;\n");
|
|
StreamPrintf(implFile, " }\n");
|
|
StreamPrintf(implFile, " ref = Malloc(sizeof(%s));\n", cType);
|
|
StreamPrintf(implFile, " if (!ref)\n");
|
|
StreamPrintf(implFile, " {\n");
|
|
StreamPrintf(implFile, " *errp = \"Unable to allocate memory for array value.\";\n");
|
|
StreamPrintf(implFile, " return 0;\n");
|
|
StreamPrintf(implFile, " }\n");
|
|
StreamPrintf(implFile, " *ref = JsonValueAs%s(v);\n", fieldType);
|
|
StreamPrintf(implFile, " ArrayAdd(out->%s, ref);\n", key);
|
|
|
|
*fieldType = tolower(*fieldType);
|
|
}
|
|
else if (StrEquals(fieldType, "string"))
|
|
{
|
|
StreamPrintf(implFile, " if (JsonValueType(v) != %s)\n", JsonTypeToStr(jsonType));
|
|
StreamPrintf(implFile, " {\n");
|
|
StreamPrintf(implFile, " *errp = \"%s.%s[] contains an invalid value.\";\n", type, key);
|
|
StreamPrintf(implFile, " return 0;\n");
|
|
StreamPrintf(implFile, " }\n");
|
|
StreamPrintf(implFile, " ArrayAdd(out->%s, StrDuplicate(JsonValueAsString(v)));\n", key);
|
|
}
|
|
else if (StrEquals(fieldType, "object"))
|
|
{
|
|
StreamPrintf(implFile, " if (JsonValueType(v) != %s)\n", JsonTypeToStr(jsonType));
|
|
StreamPrintf(implFile, " {\n");
|
|
StreamPrintf(implFile, " *errp = \"%s.%s[] contains an invalid value.\";\n", type, key);
|
|
StreamPrintf(implFile, " return 0;\n");
|
|
StreamPrintf(implFile, " }\n");
|
|
StreamPrintf(implFile, " ArrayAdd(out->%s, JsonDuplicate(JsonValueAsObject(v)));\n", key);
|
|
}
|
|
else
|
|
{
|
|
if (isEnum)
|
|
{
|
|
StreamPrintf(implFile, " int parseResult;\n");
|
|
}
|
|
StreamPrintf(implFile, " %s *parsed;\n", fieldType);
|
|
StreamPrintf(implFile, " if (JsonValueType(v) != %s)\n", JsonTypeToStr(jsonType));
|
|
StreamPrintf(implFile, " {\n");
|
|
StreamPrintf(implFile, " *errp = \"%s.%s[] contains an invalid value.\";\n", type, key);
|
|
StreamPrintf(implFile, " return 0;\n");
|
|
StreamPrintf(implFile, " }\n");
|
|
StreamPrintf(implFile, " parsed = Malloc(sizeof(%s));\n", fieldType);
|
|
StreamPrintf(implFile, " if (!parsed)\n");
|
|
StreamPrintf(implFile, " {\n");
|
|
StreamPrintf(implFile, " *errp = \"Unable to allocate memory for array value.\";\n");
|
|
StreamPrintf(implFile, " return 0;\n");
|
|
StreamPrintf(implFile, " }\n");
|
|
if (isEnum)
|
|
{
|
|
StreamPrintf(implFile, " parseResult = %sFromStr(JsonValueAsString(v));\n", fieldType);
|
|
StreamPrintf(implFile, " *parsed = parseResult;\n");
|
|
StreamPrintf(implFile, " if (parseResult == -1)\n");
|
|
}
|
|
else
|
|
{
|
|
StreamPrintf(implFile, " if (!%sFromJson(JsonValueAsObject(v), parsed, errp))\n", fieldType);
|
|
}
|
|
StreamPrintf(implFile, " {\n");
|
|
if (!isEnum)
|
|
{
|
|
StreamPrintf(implFile, " %sFree(parsed);\n", fieldType);
|
|
}
|
|
StreamPrintf(implFile, " Free(parsed);\n");
|
|
StreamPrintf(implFile, " return 0;\n");
|
|
StreamPrintf(implFile, " }\n");
|
|
StreamPrintf(implFile, " ArrayAdd(out->%s, parsed);\n", key);
|
|
}
|
|
|
|
StreamPrintf(implFile, " }\n");
|
|
StreamPrintf(implFile, " }\n");
|
|
|
|
fieldType[strlen(fieldType)] = ']';
|
|
}
|
|
else if (jsonType == JSON_OBJECT)
|
|
{
|
|
StreamPrintf(implFile, " if (!%sFromJson(JsonValueAsObject(val), &out->%s, errp))\n {\n", fieldType, key);
|
|
StreamPrintf(implFile, " return 0;\n");
|
|
StreamPrintf(implFile, " }\n");
|
|
}
|
|
else
|
|
{
|
|
char *func;
|
|
|
|
switch (jsonType)
|
|
{
|
|
case JSON_STRING:
|
|
func = "String";
|
|
break;
|
|
case JSON_INTEGER:
|
|
func = "Integer";
|
|
break;
|
|
case JSON_FLOAT:
|
|
func = "Float";
|
|
break;
|
|
case JSON_BOOLEAN:
|
|
func = "Boolean";
|
|
break;
|
|
default:
|
|
/* Should not happen */
|
|
func = NULL;
|
|
break;
|
|
}
|
|
|
|
if (jsonType == JSON_STRING)
|
|
{
|
|
if (isEnum)
|
|
{
|
|
StreamPrintf(implFile, " enumParseRes = %sFromStr(JsonValueAs%s(val));\n", fieldType, func);
|
|
StreamPrintf(implFile, " if (enumParseRes == -1)\n", key);
|
|
StreamPrintf(implFile, " {\n");
|
|
StreamPrintf(implFile, " *errp = \"Invalid value for %s.%s.\";\n", type, key);
|
|
StreamPrintf(implFile, " return 0;\n");
|
|
StreamPrintf(implFile, " }\n");
|
|
StreamPrintf(implFile, " out->%s = enumParseRes;\n", key);
|
|
}
|
|
else
|
|
{
|
|
StreamPrintf(implFile, " out->%s = StrDuplicate(JsonValueAs%s(val));\n", key, func);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
StreamPrintf(implFile, " out->%s = JsonValueAs%s(val);\n", key, func);
|
|
}
|
|
}
|
|
StreamPrintf(implFile, " }\n");
|
|
|
|
if (required)
|
|
{
|
|
StreamPrintf(implFile, " else\n {\n");
|
|
StreamPrintf(implFile, " *errp = \"%s.%s is required.\";\n", type, key);
|
|
StreamPrintf(implFile, " return 0;\n");
|
|
StreamPrintf(implFile, " }\n");
|
|
}
|
|
|
|
StreamPutc(implFile, '\n');
|
|
}
|
|
StreamPrintf(implFile, " return 1;\n");
|
|
StreamPrintf(implFile, "}\n\n");
|
|
|
|
StreamPrintf(headerFile, "extern HashMap * %sToJson(%s *);\n", type, type);
|
|
StreamPrintf(implFile, "HashMap *\n%sToJson(%s *val)\n{\n", type, type);
|
|
StreamPrintf(implFile, " HashMap *json;\n");
|
|
StreamPrintf(implFile, "\n");
|
|
StreamPrintf(implFile, " if (!val)\n");
|
|
StreamPrintf(implFile, " {\n");
|
|
StreamPrintf(implFile, " return NULL;\n");
|
|
StreamPrintf(implFile, " }\n\n");
|
|
StreamPrintf(implFile, " json = HashMapCreate();\n");
|
|
StreamPrintf(implFile, " if (!json)\n");
|
|
StreamPrintf(implFile, " {\n");
|
|
StreamPrintf(implFile, " return NULL;\n");
|
|
StreamPrintf(implFile, " }\n\n");
|
|
for (i = 0; i < ArraySize(keys); i++)
|
|
{
|
|
char *key = ArrayGet(keys, i);
|
|
char *fieldType = JsonValueAsString(JsonGet(fields, 2, key, "type"));
|
|
int isEnum = StrEquals(JsonValueAsString(JsonGet(types, 2, fieldType, "type")), "enum");
|
|
int ignore = JsonValueAsBoolean(JsonGet(fields, 2, key, "ignore"));
|
|
|
|
if (ignore)
|
|
{
|
|
StreamPrintf(implFile, " /* Ignored field: %s */\n\n", key);
|
|
continue;
|
|
}
|
|
|
|
if (StrEquals(fieldType, "array"))
|
|
{
|
|
StreamPrintf(implFile, " if (val->%s)\n", key);
|
|
StreamPrintf(implFile, " {\n");
|
|
StreamPrintf(implFile, " size_t i;\n");
|
|
StreamPrintf(implFile, " Array *jsonArr = ArrayCreate();\n");
|
|
StreamPrintf(implFile, " if (!jsonArr)\n");
|
|
StreamPrintf(implFile, " {\n");
|
|
StreamPrintf(implFile, " JsonFree(json);\n");
|
|
StreamPrintf(implFile, " return NULL;\n");
|
|
StreamPrintf(implFile, " }\n");
|
|
StreamPrintf(implFile, " for (i = 0; i < ArraySize(val->%s); i++)\n", key);
|
|
StreamPrintf(implFile, " {\n");
|
|
StreamPrintf(implFile, " ArrayAdd(jsonArr, JsonValueDuplicate(ArrayGet(val->%s, i)));\n", key);
|
|
StreamPrintf(implFile, " }\n");
|
|
StreamPrintf(implFile, " HashMapSet(json, \"%s\", JsonValueArray(jsonArr));\n", Trim('_', key));
|
|
StreamPrintf(implFile, " }\n");
|
|
}
|
|
else if (StrEquals(fieldType, "object"))
|
|
{
|
|
StreamPrintf(implFile, " HashMapSet(json, \"%s\", JsonValueObject(JsonDuplicate(val->%s)));\n", Trim('_', key), key);
|
|
}
|
|
else if (*fieldType == '[' && fieldType[strlen(fieldType) - 1] == ']')
|
|
{
|
|
int isPrimitive;
|
|
|
|
fieldType++;
|
|
fieldType[strlen(fieldType) - 1] = '\0';
|
|
isEnum = StrEquals(JsonValueAsString(JsonGet(types, 2, fieldType, "type")), "enum");
|
|
isPrimitive = StrEquals(fieldType, "integer") ||
|
|
StrEquals(fieldType, "boolean") ||
|
|
StrEquals(fieldType, "float");
|
|
|
|
|
|
StreamPrintf(implFile, " if (val->%s)\n", key);
|
|
StreamPrintf(implFile, " {\n");
|
|
StreamPrintf(implFile, " size_t i;\n");
|
|
StreamPrintf(implFile, " Array *jsonArr = ArrayCreate();\n");
|
|
StreamPrintf(implFile, " if (!jsonArr)\n");
|
|
StreamPrintf(implFile, " {\n");
|
|
StreamPrintf(implFile, " JsonFree(json);\n");
|
|
StreamPrintf(implFile, " return NULL;\n");
|
|
StreamPrintf(implFile, " }\n");
|
|
StreamPrintf(implFile, " for (i = 0; i < ArraySize(val->%s); i++)\n", key);
|
|
StreamPrintf(implFile, " {\n");
|
|
|
|
if (StrEquals(fieldType, "string"))
|
|
{
|
|
StreamPrintf(implFile, " ArrayAdd(jsonArr, JsonValueString(ArrayGet(val->%s, i)));\n", key);
|
|
}
|
|
else if (!isPrimitive)
|
|
{
|
|
StreamPrintf(implFile, " ArrayAdd(jsonArr, JsonValueObject(%sToJson(ArrayGet(val->%s, i))));\n", fieldType, key);
|
|
}
|
|
else
|
|
{
|
|
char *cType;
|
|
|
|
if (StrEquals(fieldType, "integer"))
|
|
{
|
|
cType = "Int64";
|
|
}
|
|
else if (StrEquals(fieldType, "float"))
|
|
{
|
|
cType = "double";
|
|
}
|
|
else if (StrEquals(fieldType, "boolean"))
|
|
{
|
|
cType = "int";
|
|
}
|
|
else
|
|
{
|
|
/* Should never happen */
|
|
cType = NULL;
|
|
}
|
|
|
|
*fieldType = toupper(*fieldType);
|
|
StreamPrintf(implFile, " ArrayAdd(jsonArr, (JsonValue%s(*((%s *) ArrayGet(val->%s, i)))));\n", fieldType, cType, key);
|
|
*fieldType = tolower(*fieldType);
|
|
}
|
|
|
|
StreamPrintf(implFile, " }\n");
|
|
StreamPrintf(implFile, " HashMapSet(json, \"%s\", JsonValueArray(jsonArr));\n", Trim('_', key));
|
|
StreamPrintf(implFile, " }\n");
|
|
|
|
fieldType[strlen(fieldType)] = ']';
|
|
}
|
|
else
|
|
{
|
|
if (HashMapGet(types, fieldType))
|
|
{
|
|
if (isEnum)
|
|
{
|
|
StreamPrintf(implFile, " HashMapSet(json, \"%s\", JsonValueString(%sToStr(val->%s)));\n", Trim('_', key), fieldType, key);
|
|
}
|
|
else
|
|
{
|
|
StreamPrintf(implFile, " HashMapSet(json, \"%s\", JsonValueObject(%sToJson(&val->%s)));\n", Trim('_', key), fieldType, key);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*fieldType = toupper(*fieldType);
|
|
StreamPrintf(implFile, " HashMapSet(json, \"%s\", JsonValue%s(val->%s));\n", Trim('_', key), fieldType, key);
|
|
*fieldType = tolower(*fieldType);
|
|
}
|
|
}
|
|
}
|
|
StreamPrintf(implFile, " return json;\n");
|
|
StreamPrintf(implFile, "}\n\n");
|
|
|
|
StreamPrintf(headerFile, "extern void %sFree(%s *);\n", type, type);
|
|
StreamPrintf(implFile, "void\n%sFree(%s *val)\n{\n", type, type);
|
|
StreamPrintf(implFile, " if (!val)\n");
|
|
StreamPrintf(implFile, " {\n");
|
|
StreamPrintf(implFile, " return;\n");
|
|
StreamPrintf(implFile, " }\n\n");
|
|
for (i = 0; i < ArraySize(keys); i++)
|
|
{
|
|
char *key = ArrayGet(keys, i);
|
|
char *fieldType = JsonValueAsString(JsonGet(fields, 2, key, "type"));
|
|
int isEnum = StrEquals(JsonValueAsString(JsonGet(types, 2, fieldType, "type")), "enum");
|
|
|
|
if (StrEquals(fieldType, "array"))
|
|
{
|
|
StreamPrintf(implFile, " if (val->%s)\n", key);
|
|
StreamPrintf(implFile, " {\n");
|
|
StreamPrintf(implFile, " size_t i;\n");
|
|
StreamPrintf(implFile, " for (i = 0; i < ArraySize(val->%s); i++)\n", key);
|
|
StreamPrintf(implFile, " {\n");
|
|
StreamPrintf(implFile, " JsonValueFree(ArrayGet(val->%s, i));\n", key);
|
|
StreamPrintf(implFile, " }\n");
|
|
StreamPrintf(implFile, " ArrayFree(val->%s);\n", key);
|
|
StreamPrintf(implFile, " }\n");
|
|
}
|
|
else if (StrEquals(fieldType, "object"))
|
|
{
|
|
StreamPrintf(implFile, " JsonFree(val->%s);\n", key);
|
|
}
|
|
else if (StrEquals(fieldType, "string"))
|
|
{
|
|
StreamPrintf(implFile, " Free(val->%s);\n", key);
|
|
}
|
|
else if (*fieldType == '[' && fieldType[strlen(fieldType) - 1] == ']')
|
|
{
|
|
int isPrimitive;
|
|
|
|
fieldType++;
|
|
fieldType[strlen(fieldType) - 1] = '\0';
|
|
isEnum = StrEquals(JsonValueAsString(JsonGet(types, 2, fieldType, "type")), "enum");
|
|
isPrimitive = StrEquals(fieldType, "boolean") ||
|
|
StrEquals(fieldType, "float") ||
|
|
StrEquals(fieldType, "integer") ||
|
|
StrEquals(fieldType, "string");
|
|
|
|
StreamPrintf(implFile, " if (val->%s)\n", key);
|
|
StreamPrintf(implFile, " {\n");
|
|
StreamPrintf(implFile, " size_t i;\n");
|
|
StreamPrintf(implFile, " for (i = 0; i < ArraySize(val->%s); i++)\n", key);
|
|
StreamPrintf(implFile, " {\n");
|
|
StreamPrintf(implFile, " %sFree(ArrayGet(val->%s, i));\n", (!isEnum && !isPrimitive) ? fieldType : "", key);
|
|
if (!isEnum && !isPrimitive)
|
|
{
|
|
StreamPrintf(implFile, " Free(ArrayGet(val->%s, i));\n", key);
|
|
}
|
|
StreamPrintf(implFile, " }\n");
|
|
StreamPrintf(implFile, " ArrayFree(val->%s);\n", key);
|
|
StreamPrintf(implFile, " }\n");
|
|
|
|
fieldType[strlen(fieldType)] = ']';
|
|
}
|
|
else
|
|
{
|
|
/* Ignore primitives but call the appropriate free
|
|
* method on declared types that aren't "extern". */
|
|
char *fieldTypeType = JsonValueAsString(JsonGet(types, 2, fieldType, "type"));
|
|
if (!isEnum && HashMapGet(types, fieldType) && !StrEquals(fieldTypeType, "extern"))
|
|
{
|
|
StreamPrintf(implFile, " %sFree(&val->%s);\n", fieldType, key);
|
|
}
|
|
}
|
|
}
|
|
StreamPrintf(implFile, "}\n\n");
|
|
StreamPutc(headerFile, '\n');
|
|
}
|
|
else if (StrEquals(typeType, "enum"))
|
|
{
|
|
StreamPrintf(headerFile, "extern int %sFromStr(char *);\n", type);
|
|
StreamPrintf(implFile, "int\n%sFromStr(char *str)\n{\n", type);
|
|
for (i = 0; i < ArraySize(keys); i++)
|
|
{
|
|
char *key = ArrayGet(keys, i);
|
|
|
|
if (i > 0)
|
|
{
|
|
StreamPrintf(implFile, " else if");
|
|
}
|
|
else
|
|
{
|
|
StreamPrintf(implFile, " if");
|
|
}
|
|
|
|
StreamPrintf(implFile, " (StrEquals(str, \"%s\"))\n {\n", Trim('_', key));
|
|
StreamPrintf(implFile, " return %s;\n", JsonValueAsString(JsonGet(fields, 2, key, "name")));
|
|
StreamPrintf(implFile, " }\n");
|
|
}
|
|
|
|
StreamPrintf(implFile, " else\n {\n");
|
|
StreamPrintf(implFile, " return -1;\n");
|
|
StreamPrintf(implFile, " }\n");
|
|
StreamPrintf(implFile, "}\n\n");
|
|
|
|
StreamPrintf(headerFile, "extern char * %sToStr(%s);\n", type, type);
|
|
StreamPrintf(implFile, "char *\n%sToStr(%s val)\n{\n", type, type);
|
|
StreamPrintf(implFile, " switch (val)\n {\n");
|
|
for (i = 0; i < ArraySize(keys); i++)
|
|
{
|
|
char *key = ArrayGet(keys, i);
|
|
char *name = JsonValueAsString(JsonGet(fields, 2, key, "name"));
|
|
|
|
StreamPrintf(implFile, " case %s:\n", name);
|
|
StreamPrintf(implFile, " return \"%s\";\n", Trim('_', key));
|
|
}
|
|
StreamPrintf(implFile, " default:\n");
|
|
StreamPrintf(implFile, " return NULL;\n");
|
|
StreamPrintf(implFile, " }\n");
|
|
StreamPrintf(implFile, "}\n\n");
|
|
StreamPutc(headerFile, '\n');
|
|
|
|
}
|
|
|
|
ArrayFree(keys);
|
|
}
|
|
|
|
StreamPrintf(headerFile, "#endif /* %s */\n", guard);
|
|
|
|
ret = EXIT_SUCCESS;
|
|
|
|
finish:
|
|
|
|
StreamClose(schemaFile);
|
|
StreamClose(headerFile);
|
|
StreamClose(implFile);
|
|
|
|
JsonFree(schemaJson);
|
|
|
|
return ret;
|
|
}
|