mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-23 21:02:50 +00:00
461bb9f434
It turns out labeled arrays don't work if structs aren't declared in the right order (no idea what that is, though) as the struct might not have been processed when the labeled array field is initialized. Thus, do a pro-processing pass to set up any parse data prior to writing the tables.
538 lines
13 KiB
R
538 lines
13 KiB
R
#include <hash.h>
|
|
#include <qfile.h>
|
|
#include <runtime.h>
|
|
#include <string.h>
|
|
#include <types.h>
|
|
#include <Array.h>
|
|
#include <PropertyList.h>
|
|
|
|
#include "vkfielddef.h"
|
|
#include "vkfieldtype.h"
|
|
#include "vkgen.h"
|
|
#include "vkstruct.h"
|
|
|
|
@implementation Struct
|
|
|
|
-(void) dealloc
|
|
{
|
|
[field_dict release];
|
|
[field_defs release];
|
|
[only release];
|
|
str_free (outname);
|
|
str_free (label_field);
|
|
[super dealloc];
|
|
}
|
|
|
|
-(string) name
|
|
{
|
|
return str_mid(type.strct.tag, 4);
|
|
}
|
|
|
|
-(void) addToQueue
|
|
{
|
|
string name = [self name];
|
|
if (!Hash_Find (processed_types, name)) {
|
|
//printf (" +%s\n", name);
|
|
Hash_Add (processed_types, (void *) name);
|
|
[queue addObject: self];
|
|
}
|
|
}
|
|
|
|
-(void) queueFieldTypes
|
|
{
|
|
qfot_struct_t *strct =&type.strct;
|
|
PLItem *field_dict = [parse getObjectForKey:[self name]];
|
|
int readonly = [field_dict string] == "readonly";
|
|
|
|
if (readonly) {
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < strct.num_fields; i++) {
|
|
qfot_var_t *var = &strct.fields[i];
|
|
if (field_dict) {
|
|
PLItem *item = [field_dict getObjectForKey:var.name];
|
|
FieldDef *def = [FieldDef fielddef:item
|
|
struct:self
|
|
field:var.name];
|
|
if (![def searchType]) {
|
|
continue;
|
|
}
|
|
}
|
|
Type *type = [Type findType:var.type];
|
|
[type addToQueue];
|
|
}
|
|
}
|
|
|
|
-(qfot_var_t *)findField:(string) fieldName
|
|
{
|
|
for (int i = 0; i < type.strct.num_fields; i++) {
|
|
qfot_var_t *var = &type.strct.fields[i];
|
|
if (var.name == fieldName) {
|
|
return var;
|
|
}
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
-(string)sTypeName
|
|
{
|
|
string s = "VK_STRUCTURE_TYPE";
|
|
string name = [self outname];
|
|
int length = strlen (name);
|
|
int start, end, c;
|
|
for (start = 2; start < length; start = end) {
|
|
for (end = start + 1; end < length; end++) {
|
|
c = str_char (name, end);
|
|
if (c >= 'A' && c <= 'Z') {
|
|
break;
|
|
}
|
|
}
|
|
s += "_" + str_mid (name, start, end);
|
|
}
|
|
str_free (name);
|
|
return str_upper (s);
|
|
}
|
|
|
|
-(void) setLabelField:(string)label_field
|
|
{
|
|
self.label_field = label_field;
|
|
}
|
|
|
|
-(void) writeForward
|
|
{
|
|
PLItem *field_dict = [parse getObjectForKey:[self name]];
|
|
int readonly = [field_dict string] == "readonly";
|
|
if (!readonly) {
|
|
fprintf (output_file, "static int %s (const plfield_t *field,"
|
|
" const plitem_t *item, void *data, plitem_t *messages,"
|
|
" void *context);\n",
|
|
[self parseFunc]);
|
|
}
|
|
}
|
|
|
|
static void
|
|
write_function_head (Struct *self)
|
|
{
|
|
fprintf (output_file, "static int %s (const plfield_t *field,"
|
|
" const plitem_t *item, void *data, plitem_t *messages,"
|
|
" void *context)\n",
|
|
[self parseFunc]);
|
|
fprintf (output_file, "{\n");
|
|
}
|
|
|
|
static void
|
|
write_function_tail (Struct *self)
|
|
{
|
|
fprintf (output_file, "}\n");
|
|
}
|
|
|
|
static void
|
|
write_parse_type (Struct *self)
|
|
{
|
|
fprintf (output_file, "\t\tif (!PL_ParseStruct (%s_fields, item, data, "
|
|
"messages, context)) {\n", [self outname]);
|
|
fprintf (output_file, "\t\t\treturn 0;\n");
|
|
fprintf (output_file, "\t\t}\n");
|
|
}
|
|
|
|
static void
|
|
write_auto_parse (Struct *self, string field)
|
|
{
|
|
fprintf (output_file, "\t\tdo {\n");
|
|
fprintf (output_file, "\t\t\tplfield_t *f = find_field (%s_fields, %s, "
|
|
"item, messages);\n", [self outname], sprintf ("\"%s\"", field));
|
|
fprintf (output_file, "\t\t\tif (!f) {\n");
|
|
fprintf (output_file, "\t\t\t\treturn 0;");
|
|
fprintf (output_file, "\t\t\t};\n");
|
|
fprintf (output_file, "\t\t\tf->parser (f, item, &%s, messages, context);\n",
|
|
sprintf ("((%s *) data)->%s", [self outname], field));
|
|
fprintf (output_file, "\t\t} while (0);\n");
|
|
}
|
|
|
|
static int
|
|
check_need_table (Struct *self, PLItem *field_dict, string type)
|
|
{
|
|
string key = nil;
|
|
switch (type) {
|
|
case "QFDictionary": key = ".dictionary"; break;
|
|
case "QFArray": key = ".array"; break;
|
|
case "QFBinary": key = ".binary"; break;
|
|
case "QFString": key = ".string"; break;
|
|
}
|
|
PLItem *type_obj = [field_dict getObjectForKey:key];
|
|
int count = [type_obj numKeys];
|
|
if (!count) {
|
|
return 0;
|
|
}
|
|
for (int i = 0; i < count; i++) {
|
|
string field = [type_obj keyAtIndex:i];
|
|
PLItem *item = [type_obj getObjectForKey:field];
|
|
string str = [item string];
|
|
|
|
if (field == ".parse") {
|
|
return 1;
|
|
}
|
|
if (str == "$auto") {
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
write_type (Struct *self, PLItem *field_dict, string type)
|
|
{
|
|
string key = nil;
|
|
switch (type) {
|
|
case "QFDictionary": key = ".dictionary"; break;
|
|
case "QFArray": key = ".array"; break;
|
|
case "QFBinary": key = ".binary"; break;
|
|
case "QFString": key = ".string"; break;
|
|
}
|
|
PLItem *type_obj = [field_dict getObjectForKey:key];
|
|
int count = [type_obj numKeys];
|
|
if (!count) {
|
|
//FIXME errors
|
|
return;
|
|
}
|
|
fprintf (output_file, "\tif (type == %s) {\n", type);
|
|
for (int i = 0; i < count; i++) {
|
|
string field = [type_obj keyAtIndex:i];
|
|
PLItem *item = [type_obj getObjectForKey:field];
|
|
string str = [item string];
|
|
|
|
if (field == ".parse") {
|
|
write_parse_type (self);
|
|
continue;
|
|
}
|
|
|
|
switch (str) {
|
|
case "$item.string":
|
|
str = "vkstrdup (context, PL_String (item))";
|
|
break;
|
|
case "$item.line":
|
|
str = "PL_Line (item)";
|
|
break;
|
|
case "$name":
|
|
str = "vkstrdup (context, field->name)";
|
|
break;
|
|
case "$index":
|
|
str = "field->offset";
|
|
break;
|
|
case "$auto":
|
|
write_auto_parse (self, field);
|
|
continue;
|
|
}
|
|
fprintf (output_file, "\t\t((%s *) data)->%s = %s;\n", [self outname],
|
|
field, str);
|
|
}
|
|
fprintf (output_file, "\t\treturn 1;\n");
|
|
fprintf (output_file, "\t}\n");
|
|
}
|
|
|
|
static void
|
|
write_parser (Struct *self, int have_sType, PLItem *only)
|
|
{
|
|
write_function_head (self);
|
|
if (have_sType) {
|
|
fprintf (output_file, "\t((%s *) data)->sType", [self outname]);
|
|
fprintf (output_file, " = %s;\n", [self sTypeName]);
|
|
}
|
|
if (self.label_field) {
|
|
fprintf (output_file, "\t((%s *) data)->%s", [self outname],
|
|
self.label_field);
|
|
fprintf (output_file, " = vkstrdup (context, field->name);\n");
|
|
}
|
|
if (only) {
|
|
fprintf (output_file, "\tplfield_t *f = &%s_fields[0];\n",
|
|
[self outname]);
|
|
fprintf (output_file,
|
|
"\tif (!PL_CheckType (PL_Type (item), f->type)) {\n"
|
|
"\t\tPL_TypeMismatch (messages, item, "
|
|
"f->name, f->type, PL_Type (item));\n"
|
|
"\t\treturn 0;\n"
|
|
"\t}\n"
|
|
"\tvoid *flddata = (byte *)data + f->offset;\n"
|
|
"\treturn f->parser (f, item, flddata, messages, "
|
|
"context);\n");
|
|
} else {
|
|
fprintf (output_file,
|
|
"\tif (PL_Type (item) == QFString\n"
|
|
"\t\t&& !(item = parse_reference (item, \"%s\", "
|
|
"messages, context))) {\n"
|
|
"\t\treturn 0;\n"
|
|
"\t}\n"
|
|
"\treturn PL_ParseStruct (%s_fields, item, data, "
|
|
"messages, context);\n",
|
|
[self outname], [self outname]);
|
|
}
|
|
write_function_tail (self);
|
|
}
|
|
|
|
static void
|
|
write_cexpr (Struct *self, Array *field_defs)
|
|
{
|
|
fprintf (output_file, "static exprsym_t %s_symbols[] = {\n",
|
|
[self outname]);
|
|
if (field_defs) {
|
|
PLItem *field_def;
|
|
qfot_var_t *field;
|
|
|
|
for (int i = [field_defs count]; i-- > 0; ) {
|
|
FieldDef *field_def = [field_defs objectAtIndex:i];
|
|
[field_def writeSymbol];
|
|
}
|
|
} else {
|
|
for (int i = 0; i < self.type.strct.num_fields; i++) {
|
|
qfot_var_t *field = &self.type.strct.fields[i];
|
|
if (field.name == "sType" || field.name == "pNext") {
|
|
continue;
|
|
}
|
|
Type *field_type = [Type findType: field.type];
|
|
fprintf (output_file,
|
|
"\t{\"%s\", &%s, (void *) field_offset (%s, %s)},\n",
|
|
field.name, [field_type cexprType], [self outname],
|
|
field.name);
|
|
}
|
|
}
|
|
fprintf (output_file, "\t{ }\n");
|
|
fprintf (output_file, "};\n");
|
|
|
|
fprintf (output_file, "static exprtab_t %s_symtab = {\n", [self outname]);
|
|
fprintf (output_file, "\t%s_symbols,\n", [self outname]);
|
|
fprintf (output_file, "};\n");
|
|
|
|
fprintf (output_file, "exprtype_t %s_type = {\n", [self outname]);
|
|
fprintf (output_file, "\t.name = \"%s\",\n", [self outname]);
|
|
fprintf (output_file, "\t.size = sizeof (%s),\n", [self outname]);
|
|
fprintf (output_file, "\t.binops = cexpr_struct_binops,\n");
|
|
fprintf (output_file, "\t.unops = 0,\n");
|
|
fprintf (output_file, "\t.data = &%s_symtab,\n", [self outname]);
|
|
fprintf (output_file, "};\n");
|
|
fprintf (output_file, "\n");
|
|
fprintf (header_file, "extern exprtype_t %s_type;\n", [self outname]);
|
|
}
|
|
|
|
static void
|
|
write_table (Struct *self, PLItem *field_dict, Array *field_defs,
|
|
PLItem *only, int need_parser)
|
|
{
|
|
qfot_type_t *type = self.type;
|
|
int have_sType = 0;
|
|
int have_pNext = 0;
|
|
int readonly = [field_dict string] == "readonly";
|
|
|
|
for (int i = 0; i < type.strct.num_fields; i++) {
|
|
qfot_var_t *field = &type.strct.fields[i];
|
|
if (field.name == "sType") {
|
|
have_sType = 1;
|
|
}
|
|
if (field.name == "pNext") {
|
|
have_pNext = 1;
|
|
self.write_symtab = 1;
|
|
}
|
|
}
|
|
for (int i = [field_defs count]; i-- > 0; ) {
|
|
FieldDef *field_def = [field_defs objectAtIndex:i];
|
|
[field_def writeParseData];
|
|
}
|
|
if (!readonly) {
|
|
fprintf (output_file, "static plfield_t %s_fields[] = {\n",
|
|
[self outname]);
|
|
if (!only) {
|
|
fprintf (output_file,
|
|
"\t{\"@inherit\", 0, QFString, parse_inherit, "
|
|
"&%s_fields},\n", [self outname]);
|
|
}
|
|
if (have_pNext) {
|
|
fprintf (output_file,
|
|
"\t{\"@next\", field_offset (%s, pNext), "
|
|
"QFArray, parse_next, 0},", [self outname]);
|
|
}
|
|
for (int i = [field_defs count]; i-- > 0; ) {
|
|
FieldDef *field_def = [field_defs objectAtIndex:i];
|
|
[field_def writeField];
|
|
}
|
|
fprintf (output_file, "\t{ }\n");
|
|
fprintf (output_file, "};\n");
|
|
|
|
if (need_parser) {
|
|
write_parser (self, have_sType, only);
|
|
}
|
|
if (have_pNext) {
|
|
fprintf (output_file, "static parserref_t %s_parser = ",
|
|
[self outname]);
|
|
fprintf (output_file, "{\"%s\", %s, sizeof(%s)};\n",
|
|
[self outname], [self parseFunc], [self outname]);
|
|
}
|
|
}
|
|
}
|
|
|
|
-(void) initParse:(PLItem *)parse
|
|
{
|
|
if ([parse string] == "skip") {
|
|
skip = 1;
|
|
return;
|
|
}
|
|
|
|
field_dict = [parse retain];
|
|
|
|
PLItem *new_name = [field_dict getObjectForKey:".name"];
|
|
if (new_name) {
|
|
outname = str_hold ([new_name string]);
|
|
}
|
|
|
|
field_defs = [[Array array] retain];
|
|
|
|
only = [[field_dict getObjectForKey:".only"] retain];
|
|
if (only) {
|
|
string field_name = [only string];
|
|
qfot_var_t *field = nil;
|
|
for (int i = 0; i < type.strct.num_fields; i++) {
|
|
qfot_var_t *f = &type.strct.fields[i];
|
|
if (f.name == field_name) {
|
|
field = f;
|
|
break;
|
|
}
|
|
}
|
|
Type *field_type = [Type findType: field.type];
|
|
FieldDef *field_def = [field_type fielddef:self field:field.name];
|
|
if (!field_def) {
|
|
field_def = [FieldDef fielddef:nil
|
|
struct:self
|
|
field:field.name];
|
|
}
|
|
[field_defs addObject: field_def];
|
|
} else if (field_dict) {
|
|
PLItem *field_keys = [field_dict allKeys];
|
|
|
|
for (int i = [field_keys count]; i-- > 0; ) {
|
|
string field_name = [[field_keys getObjectAtIndex:i] string];
|
|
|
|
if (str_mid(field_name, 0, 1) == ".") {
|
|
continue;
|
|
}
|
|
PLItem *field_item = [field_dict getObjectForKey:field_name];
|
|
FieldDef *field_def = [FieldDef fielddef:field_item
|
|
struct:self
|
|
field:field_name];
|
|
[field_defs addObject: field_def];
|
|
}
|
|
} else {
|
|
for (int i = 0; i < type.strct.num_fields; i++) {
|
|
qfot_var_t *field = &type.strct.fields[i];
|
|
if (field.name == "sType" || field.name == "pNext") {
|
|
continue;
|
|
}
|
|
Type *field_type = [Type findType: field.type];
|
|
FieldDef *field_def = [field_type fielddef:self field:field.name];
|
|
if (!field_def) {
|
|
field_def = [FieldDef fielddef:nil
|
|
struct:self
|
|
field:field.name];
|
|
}
|
|
[field_defs addObject: field_def];
|
|
}
|
|
}
|
|
}
|
|
|
|
-(void) writeTable
|
|
{
|
|
if (skip) {
|
|
return;
|
|
}
|
|
|
|
if ([field_dict getObjectForKey:".type"]) {
|
|
PLItem *type = [field_dict getObjectForKey:".type"];
|
|
string str = [type string];
|
|
int need_table = 0;
|
|
|
|
if (str) {
|
|
need_table |= check_need_table (self, field_dict, str);
|
|
} else {
|
|
for (int i = [type count]; i-- > 0; ) {
|
|
string str = [[type getObjectAtIndex:i] string];
|
|
need_table |= check_need_table (self, field_dict, str);
|
|
}
|
|
}
|
|
if (need_table) {
|
|
write_table (self, field_dict, field_defs, only, 0);
|
|
}
|
|
write_function_head (self);
|
|
fprintf (output_file, "\tpltype_t type = PL_Type (item);\n");
|
|
if (str) {
|
|
write_type (self, field_dict, str);
|
|
} else {
|
|
for (int i = [type count]; i-- > 0; ) {
|
|
str = [[type getObjectAtIndex:i] string];
|
|
write_type (self, field_dict, str);
|
|
}
|
|
}
|
|
fprintf (output_file,
|
|
"\tPL_TypeMismatch (messages, item, field->name, %s, type);\n",
|
|
parseItemType (type));
|
|
fprintf (output_file, "\treturn 0;\n");
|
|
write_function_tail (self);
|
|
write_cexpr (self, field_defs);
|
|
return;
|
|
}
|
|
|
|
write_table (self, field_dict, field_defs, only, 1);
|
|
|
|
write_cexpr (self, field_defs);
|
|
}
|
|
|
|
-(void) writeSymtabInit
|
|
{
|
|
PLItem *field_dict = [parse getObjectForKey:[self outname]];
|
|
|
|
if ([parse string] == "skip") {
|
|
return;
|
|
}
|
|
|
|
fprintf (output_file, "\tcexpr_init_symtab (&%s_symtab, context);\n",
|
|
[self outname]);
|
|
}
|
|
|
|
-(void) writeSymtabEntry
|
|
{
|
|
if (!write_symtab || [parse string] == "skip") {
|
|
return;
|
|
}
|
|
fprintf (output_file,
|
|
"\tHash_Add (parser_table, &%s_parser);\n",
|
|
[self outname]);
|
|
}
|
|
|
|
-(string) outname
|
|
{
|
|
if (outname) {
|
|
return outname;
|
|
}
|
|
if (alias) {
|
|
return [alias name];
|
|
}
|
|
return [self name];
|
|
}
|
|
|
|
-(string) cexprType
|
|
{
|
|
return [self outname] + "_type";
|
|
}
|
|
|
|
-(string) parseType
|
|
{
|
|
return "QFMultiType | (1 << QFString) | (1 << QFDictionary)";
|
|
}
|
|
|
|
-(string) parseFunc
|
|
{
|
|
return "parse_" + [self outname];
|
|
}
|
|
|
|
-(string) parseData
|
|
{
|
|
return "0";
|
|
}
|
|
@end
|