[qfcc] Support C's full type system

Along with QuakeC's, of course. This fixes type typeredef2 test (a lot
of work for one little syntax error). Unfortunately, it came at the cost
of requiring `>>` in front of state expressions on C-style functions
(QuakeC-style functions are unaffected). Also, there are now two
shift/reduce conflicts with structs and unions (but these same conflicts
are in gcc 3.4).

This has highlighted the need for having the equivalent of the
expression tree for the declaration system as there are now several
hacks to deal with the separation of types and declarators. But that's a
job for another week.

The grammar constructs for declarations come from gcc 3.4's parser (I
think it's the last version of gcc that used bison. Also, 3.4 is still
GPL 2, so no chance of an issue there).
This commit is contained in:
Bill Currie 2023-02-08 11:18:42 +09:00
parent 02432311c5
commit fc7c96d208
11 changed files with 920 additions and 787 deletions

View File

@ -148,7 +148,7 @@ param_t *param_append_identifiers (param_t *params, struct symbol_s *idents,
param_t *reverse_params (param_t *params);
param_t *append_params (param_t *params, param_t *more_params);
param_t *copy_params (param_t *params);
struct type_s *parse_params (struct type_s *type, param_t *params);
struct type_s *parse_params (struct type_s *return_type, param_t *params);
param_t *check_params (param_t *params);
enum storage_class_e;

View File

@ -43,6 +43,8 @@ typedef struct {
void (*emit) (struct def_s *def, void *data, int index);
} struct_def_t;
struct symtab_s *start_struct (int *su, struct symbol_s *tag,
struct symtab_s *parent);
struct symbol_s *find_struct (int su, struct symbol_s *tag,
struct type_s *type);
struct symbol_s *build_struct (int su, struct symbol_s *tag,

View File

@ -244,6 +244,10 @@ symtab_t *symtab_flat_copy (symtab_t *symtab, symtab_t *parent);
symbol_t *make_symbol (const char *name, struct type_s *type,
struct defspace_s *space, enum storage_class_e storage);
struct specifier_s;
symbol_t *declare_symbol (struct specifier_s spec, struct expr_s *init,
symtab_t *symtab);
///@}
#endif//__symtab_h

View File

@ -90,10 +90,10 @@ typedef struct type_s {
struct def_s *type_def; ///< offset of qfo encodoing
} type_t;
typedef struct {
typedef struct specifier_s {
type_t *type;
struct param_s *params;
struct symbol_s *sym; ///< for dealing with "int id" etc
struct symbol_s *sym;
storage_class_t storage;
union {
struct {
@ -105,6 +105,7 @@ typedef struct {
unsigned is_long:1;
unsigned is_typedef:1;
unsigned is_overload:1;
unsigned is_function:1;//FIXME do proper void(*)() -> ev_func
unsigned nosave:1;
unsigned no_va_list:1;
unsigned void_return:1;
@ -156,6 +157,8 @@ void chain_type (type_t *type);
level.
*/
type_t *append_type (type_t *type, type_t *new);
void set_func_type_attrs (type_t *func, specifier_t spec);
specifier_t default_type (specifier_t spec, struct symbol_s *sym);
type_t *find_type (type_t *new);
void new_typedef (const char *name, type_t *type);
type_t *field_type (type_t *aux);

View File

@ -100,7 +100,7 @@ new_param (const char *selector, type_t *type, const char *name)
ALLOC (4096, param_t, params, param);
param->next = 0;
param->selector = selector;
param->type = type;
param->type = find_type (type);
param->name = name;
return param;
@ -173,23 +173,23 @@ copy_params (param_t *params)
}
type_t *
parse_params (type_t *type, param_t *parms)
parse_params (type_t *return_type, param_t *parms)
{
param_t *p;
type_t *new;
type_t *ptype;
int count = 0;
if (type && is_class (type)) {
if (return_type && is_class (return_type)) {
error (0, "cannot return an object (forgot *?)");
type = &type_id;
return_type = &type_id;
}
new = new_type ();
new->type = ev_func;
new->alignment = 1;
new->width = 1;
new->t.func.type = type;
new->t.func.type = return_type;
new->t.func.num_params = 0;
for (p = parms; p; p = p->next) {

View File

@ -357,7 +357,7 @@ static keyword_t rua_keywords[] = {
// If not compiling for the QuakeForge VM, or if Ruamoko has been disabled,
// then they will be unavailable as keywords.
static keyword_t obj_keywords[] = {
{"id", OBJECT, .spec = { .type = &type_id } },
{"id", OBJECT_NAME, .spec = { .type = &type_id } },
{"Class", TYPE_SPEC, .spec = { .type = &type_Class } },
{"Method", TYPE_SPEC, .spec = { .type = &type_method } },
{"Super", TYPE_SPEC, .spec = { .type = &type_super } },
@ -462,7 +462,7 @@ process_keyword (keyword_t *keyword, const char *token)
{
if (keyword->value == STRUCT) {
qc_yylval.op = token[0];
} else if (keyword->value == OBJECT) {
} else if (keyword->value == OBJECT_NAME) {
symbol_t *sym;
sym = symtab_lookup (current_symtab, token);
@ -472,7 +472,11 @@ process_keyword (keyword_t *keyword, const char *token)
// be redefinable when in an enclosing scope.
if (sym->sy_type == sy_name) {
// this is the global id (object)
return OBJECT;
qc_yylval.spec = (specifier_t) {
.type = sym->type,
.sym = sym,
};
return OBJECT_NAME;
} else if (sym->sy_type == sy_type) {
// id has been redeclared via a typedef
qc_yylval.spec = (specifier_t) {

File diff suppressed because it is too large Load Diff

View File

@ -98,6 +98,33 @@ find_tag (ty_meta_e meta, symbol_t *tag, type_t *type)
return sym;
}
symtab_t *
start_struct (int *su, symbol_t *tag, symtab_t *parent)
{
symbol_t *sym;
sym = find_struct (*su, tag, 0);
if (!sym->table) {
symtab_addsymbol (parent, sym);
} else {
if (!sym->type) {
internal_error (0, "broken structure symbol?");
}
if (tag) {
tag->type = sym->type;
}
if (sym->type->meta == ty_enum
|| (sym->type->meta == ty_struct && sym->type->t.symtab)) {
error (0, "%s %s redefined",
*su == 's' ? "struct" : "union", tag->name);
*su = 0;
} else if (sym->type->meta != ty_struct) {
internal_error (0, "%s is not a struct or union",
tag->name);
}
}
return new_symtab (parent, stab_struct);
}
symbol_t *
find_struct (int su, symbol_t *tag, type_t *type)
{

View File

@ -44,6 +44,7 @@
#include "tools/qfcc/include/function.h"
#include "tools/qfcc/include/qfcc.h"
#include "tools/qfcc/include/reloc.h"
#include "tools/qfcc/include/shared.h"
#include "tools/qfcc/include/strpool.h"
#include "tools/qfcc/include/symtab.h"
#include "tools/qfcc/include/type.h"
@ -243,3 +244,56 @@ make_symbol (const char *name, type_t *type, defspace_t *space,
sym->sy_type = sy_var;
return sym;
}
symbol_t *
declare_symbol (specifier_t spec, expr_t *init, symtab_t *symtab)
{
symbol_t *s = spec.sym;
defspace_t *space = symtab->space;
if (s->table) {
// due to the way declarations work, we need a new symbol at all times.
// redelcarations will be checked later
s = new_symbol (s->name);
}
spec = default_type (spec, s);
if (!spec.storage) {
spec.storage = current_storage;
}
if (spec.storage == sc_static) {
space = pr.near_data;
}
//FIXME is_function is bad (this whole implementation of handling
//function prototypes is bad)
if (spec.is_function && is_func (spec.type)) {
set_func_type_attrs (spec.type, spec);
}
s->type = spec.type;
if (spec.is_typedef) {
if (init) {
error (0, "typedef %s is initialized", s->name);
}
s->sy_type = sy_type;
s->type = find_type (s->type);
s->type = find_type (alias_type (s->type, s->type, s->name));
symtab_addsymbol (symtab, s);
} else {
if (spec.is_function && is_func (s->type)) {
if (init) {
error (0, "function %s is initialized", s->name);
}
s->type = find_type (s->type);
s = function_symbol (s, spec.is_overload, 1);
} else {
s->type = find_type (s->type);
initialize_def (s, init, space, spec.storage, symtab);
if (s->s.def) {
s->s.def->nosave |= spec.nosave;
}
}
}
return s;
}

View File

@ -436,6 +436,82 @@ types_same (type_t *a, type_t *b)
internal_error (0, "we be broke");
}
void
set_func_type_attrs (type_t *func, specifier_t spec)
{
func->t.func.no_va_list = spec.no_va_list;
func->t.func.void_return = spec.void_return;
}
specifier_t
default_type (specifier_t spec, symbol_t *sym)
{
if (spec.type) {
if (is_float (spec.type) && !spec.multi_type) {
// no modifieres allowed
if (spec.is_unsigned) {
spec.multi_type = 1;
error (0, "both unsigned and float in declaration specifiers");
} else if (spec.is_signed) {
spec.multi_type = 1;
error (0, "both signed and float in declaration specifiers");
} else if (spec.is_short) {
spec.multi_type = 1;
error (0, "both short and float in declaration specifiers");
} else if (spec.is_long) {
spec.multi_type = 1;
error (0, "both long and float in declaration specifiers");
}
}
if (is_double (spec.type)) {
// long is allowed but ignored
if (spec.is_unsigned) {
spec.multi_type = 1;
error (0, "both unsigned and double in declaration specifiers");
} else if (spec.is_signed) {
spec.multi_type = 1;
error (0, "both signed and double in declaration specifiers");
} else if (spec.is_short) {
spec.multi_type = 1;
error (0, "both short and double in declaration specifiers");
}
}
if (is_int (spec.type)) {
// signed and short are ignored
if (spec.is_unsigned && spec.is_long) {
spec.type = &type_ulong;
} else if (spec.is_long) {
spec.type = &type_long;
}
}
} else {
if (spec.is_long) {
if (spec.is_unsigned) {
spec.type = type_ulong_uint;
} else {
spec.type = type_long_int;
}
} else {
if (spec.is_unsigned) {
spec.type = &type_uint;
} else if (spec.is_signed || spec.is_short) {
spec.type = &type_int;
}
}
}
if (!spec.type) {
spec.type = type_default;
if (sym) {
warning (0, "type defaults to '%s' in declaration of '%s'",
type_default->name, sym->name);
} else {
warning (0, "type defaults to '%s' in abstract declaration",
type_default->name);
}
}
return spec;
}
/*
find_type

View File

@ -10,7 +10,7 @@ $frame frame0 frame1 frame2 frame3
void
state0 (void)
[$frame1, state1]
>> [$frame1, state1]
{
if (self.frame != $frame1 || self.think != state1
|| self.nextthink != 0.1f) {
@ -21,7 +21,7 @@ state0 (void)
void
state1 (void)
[$frame2, state2, 0.2f]
>> [$frame2, state2, 0.2f]
{
if (self.frame != $frame2 || self.think != state2
|| self.nextthink != 0.2f) {
@ -32,7 +32,7 @@ state1 (void)
void
state2 (void)
[$frame0, state0, 0.5f]
>> [$frame0, state0, 0.5f]
{
if (self.frame != $frame0 || self.think != state0
|| self.nextthink != 0.5f) {