Implementing typedef

This commit is contained in:
Wolfgang (Blub) Bumiller 2012-11-25 15:21:52 +01:00
parent a1b50603e5
commit b851f542ac

142
parser.c
View file

@ -32,6 +32,7 @@
#define PARSER_HT_LOCALS 2 #define PARSER_HT_LOCALS 2
#define PARSER_HT_SIZE 1024 #define PARSER_HT_SIZE 1024
#define TYPEDEF_HT_SIZE 16
typedef struct { typedef struct {
lex_file *lex; lex_file *lex;
@ -60,10 +61,13 @@ typedef struct {
ht *variables; ht *variables;
ht htfields; ht htfields;
ht htglobals; ht htglobals;
ht *typedefs;
/* not to be used directly, we use the hash table */ /* not to be used directly, we use the hash table */
ast_expression **_locals; ast_expression **_locals;
size_t *_blocklocals; size_t *_blocklocals;
ast_value **_typedefs;
size_t *_blocktypedefs;
size_t errors; size_t errors;
@ -80,7 +84,7 @@ typedef struct {
static void parser_enterblock(parser_t *parser); static void parser_enterblock(parser_t *parser);
static bool parser_leaveblock(parser_t *parser); static bool parser_leaveblock(parser_t *parser);
static void parser_addlocal(parser_t *parser, const char *name, ast_expression *e); static void parser_addlocal(parser_t *parser, const char *name, ast_expression *e);
static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, bool is_const); static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, bool is_const, ast_value *cached_typedef);
static ast_block* parse_block(parser_t *parser, bool warnreturn); static ast_block* parse_block(parser_t *parser, bool warnreturn);
static bool parse_block_into(parser_t *parser, ast_block *block, bool warnreturn); static bool parse_block_into(parser_t *parser, ast_block *block, bool warnreturn);
static ast_expression* parse_statement_or_block(parser_t *parser); static ast_expression* parse_statement_or_block(parser_t *parser);
@ -332,6 +336,20 @@ static ast_expression* parser_find_var(parser_t *parser, const char *name)
return v; return v;
} }
static ast_value* parser_find_typedef(parser_t *parser, const char *name)
{
size_t i, hash;
ast_value *e;
hash = util_hthash(parser->typedefs[0], name);
for (i = vec_size(parser->typedefs); i > 0;) {
--i;
if ( (e = (ast_value*)util_htgeth(parser->typedefs[i], name, hash)) )
return e;
}
return NULL;
}
typedef struct typedef struct
{ {
size_t etype; /* 0 = expression, others are operators */ size_t etype; /* 0 = expression, others are operators */
@ -1576,12 +1594,14 @@ static void parser_enterblock(parser_t *parser)
{ {
vec_push(parser->variables, util_htnew(PARSER_HT_SIZE)); vec_push(parser->variables, util_htnew(PARSER_HT_SIZE));
vec_push(parser->_blocklocals, vec_size(parser->_locals)); vec_push(parser->_blocklocals, vec_size(parser->_locals));
vec_push(parser->typedefs, util_htnew(TYPEDEF_HT_SIZE));
vec_push(parser->_blocktypedefs, vec_size(parser->_typedefs));
} }
static bool parser_leaveblock(parser_t *parser) static bool parser_leaveblock(parser_t *parser)
{ {
bool rv = true; bool rv = true;
size_t locals; size_t locals, typedefs;
if (vec_size(parser->variables) <= PARSER_HT_LOCALS) { if (vec_size(parser->variables) <= PARSER_HT_LOCALS) {
parseerror(parser, "internal error: parser_leaveblock with no block"); parseerror(parser, "internal error: parser_leaveblock with no block");
@ -1607,6 +1627,14 @@ static bool parser_leaveblock(parser_t *parser)
} }
} }
typedefs = vec_last(parser->_blocktypedefs);
while (vec_size(parser->_typedefs) != typedefs) {
ast_delete(vec_last(parser->_typedefs));
vec_pop(parser->_typedefs);
}
util_htdel(vec_last(parser->typedefs));
vec_pop(parser->typedefs);
return rv; return rv;
} }
@ -1811,8 +1839,9 @@ static bool parse_dowhile(parser_t *parser, ast_block *block, ast_expression **o
static bool parse_for(parser_t *parser, ast_block *block, ast_expression **out) static bool parse_for(parser_t *parser, ast_block *block, ast_expression **out)
{ {
ast_loop *aloop; ast_loop *aloop;
ast_expression *initexpr, *cond, *increment, *ontrue; ast_expression *initexpr, *cond, *increment, *ontrue;
ast_value *typevar;
bool retval = true; bool retval = true;
lex_ctx ctx = parser_ctx(parser); lex_ctx ctx = parser_ctx(parser);
@ -1835,7 +1864,11 @@ static bool parse_for(parser_t *parser, ast_block *block, ast_expression **out)
goto onerr; goto onerr;
} }
if (parser->tok == TOKEN_TYPENAME) { typevar = NULL;
if (parser->tok == TOKEN_IDENT)
typevar = parser_find_typedef(parser, parser_tokval(parser));
if (typevar || parser->tok == TOKEN_TYPENAME) {
if (opts_standard != COMPILER_GMQCC) { if (opts_standard != COMPILER_GMQCC) {
if (parsewarning(parser, WARN_EXTENSIONS, if (parsewarning(parser, WARN_EXTENSIONS,
"current standard does not allow variable declarations in for-loop initializers")) "current standard does not allow variable declarations in for-loop initializers"))
@ -1844,7 +1877,7 @@ static bool parse_for(parser_t *parser, ast_block *block, ast_expression **out)
parseerror(parser, "TODO: assignment of new variables to be non-const"); parseerror(parser, "TODO: assignment of new variables to be non-const");
goto onerr; goto onerr;
if (!parse_variable(parser, block, true, false)) if (!parse_variable(parser, block, true, false, typevar))
goto onerr; goto onerr;
} }
else if (parser->tok != ';') else if (parser->tok != ';')
@ -2133,7 +2166,11 @@ static bool parse_switch(parser_t *parser, ast_block *block, ast_expression **ou
static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out, bool allow_cases) static bool parse_statement(parser_t *parser, ast_block *block, ast_expression **out, bool allow_cases)
{ {
if (parser->tok == TOKEN_TYPENAME || parser->tok == '.') ast_value *typevar = NULL;
if (parser->tok == TOKEN_IDENT)
typevar = parser_find_typedef(parser, parser_tokval(parser));
if (typevar || parser->tok == TOKEN_TYPENAME || parser->tok == '.')
{ {
/* local variable */ /* local variable */
if (!block) { if (!block) {
@ -2144,7 +2181,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
if (parsewarning(parser, WARN_EXTENSIONS, "missing 'local' keyword when declaring a local variable")) if (parsewarning(parser, WARN_EXTENSIONS, "missing 'local' keyword when declaring a local variable"))
return false; return false;
} }
if (!parse_variable(parser, block, false, false)) if (!parse_variable(parser, block, false, false, typevar))
return false; return false;
*out = NULL; *out = NULL;
return true; return true;
@ -2161,7 +2198,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
parseerror(parser, "expected variable declaration"); parseerror(parser, "expected variable declaration");
return false; return false;
} }
if (!parse_variable(parser, block, true, false)) if (!parse_variable(parser, block, true, false, NULL))
return false; return false;
*out = NULL; *out = NULL;
return true; return true;
@ -2983,7 +3020,7 @@ cleanup:
return false; return false;
} }
static ast_value *parse_typename(parser_t *parser, ast_value **storebase); static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef);
static ast_value *parse_parameter_list(parser_t *parser, ast_value *var) static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
{ {
lex_ctx ctx; lex_ctx ctx;
@ -3034,7 +3071,7 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
else else
{ {
/* for anything else just parse a typename */ /* for anything else just parse a typename */
param = parse_typename(parser, NULL); param = parse_typename(parser, NULL, NULL);
if (!param) if (!param)
goto on_error; goto on_error;
vec_push(params, param); vec_push(params, param);
@ -3144,7 +3181,7 @@ static ast_value *parse_arraysize(parser_t *parser, ast_value *var)
* void() foo(), bar * void() foo(), bar
* then the type-information 'void()' can be stored in 'storebase' * then the type-information 'void()' can be stored in 'storebase'
*/ */
static ast_value *parse_typename(parser_t *parser, ast_value **storebase) static ast_value *parse_typename(parser_t *parser, ast_value **storebase, ast_value *cached_typedef)
{ {
ast_value *var, *tmp; ast_value *var, *tmp;
lex_ctx ctx; lex_ctx ctx;
@ -3163,14 +3200,20 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase)
parseerror(parser, "expected typename for field definition"); parseerror(parser, "expected typename for field definition");
return NULL; return NULL;
} }
if (parser->tok != TOKEN_TYPENAME) { if (parser->tok == TOKEN_IDENT)
cached_typedef = parser_find_typedef(parser, parser_tokval(parser));
if (!cached_typedef && parser->tok != TOKEN_TYPENAME) {
parseerror(parser, "expected typename"); parseerror(parser, "expected typename");
return NULL; return NULL;
} }
} }
/* generate the basic type value */ /* generate the basic type value */
var = ast_value_new(ctx, "<type>", parser_token(parser)->constval.t); if (cached_typedef) {
var = ast_value_copy(cached_typedef);
ast_value_set_name(var, "<type(from_def)>");
} else
var = ast_value_new(ctx, "<type>", parser_token(parser)->constval.t);
/* do not yet turn into a field - remember: /* do not yet turn into a field - remember:
* .void() foo; is a field too * .void() foo; is a field too
* .void()() foo; is a function * .void()() foo; is a function
@ -3260,7 +3303,47 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase)
return var; return var;
} }
static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, bool is_const) static bool parse_typedef(parser_t *parser)
{
ast_value *typevar, *oldtype;
ast_expression *old;
typevar = parse_typename(parser, NULL, NULL);
if (!typevar)
return false;
if ( (old = parser_find_var(parser, typevar->name)) ) {
parseerror(parser, "cannot define a type with the same name as a variable: %s\n"
" -> `%s` has been declared here: %s:%i",
typevar->name, ast_ctx(old).file, ast_ctx(old).line);
ast_delete(typevar);
return false;
}
if ( (oldtype = parser_find_typedef(parser, typevar->name)) ) {
parseerror(parser, "type `%s` has already been declared here: %s:%i",
typevar->name, ast_ctx(oldtype).file, ast_ctx(oldtype).line);
ast_delete(typevar);
return false;
}
vec_push(parser->_typedefs, typevar);
util_htset(vec_last(parser->typedefs), typevar->name, typevar);
if (parser->tok != ';') {
parseerror(parser, "expected semicolon after typedef");
return false;
}
if (!parser_next(parser)) {
parseerror(parser, "parse error after typedef");
return false;
}
return true;
}
static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, bool is_const, ast_value *cached_typedef)
{ {
ast_value *var; ast_value *var;
ast_value *proto; ast_value *proto;
@ -3278,7 +3361,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
ast_member *me[3]; ast_member *me[3];
/* get the first complete variable */ /* get the first complete variable */
var = parse_typename(parser, &basetype); var = parse_typename(parser, &basetype, cached_typedef);
if (!var) { if (!var) {
if (basetype) if (basetype)
ast_delete(basetype); ast_delete(basetype);
@ -3754,9 +3837,13 @@ cleanup:
static bool parser_global_statement(parser_t *parser) static bool parser_global_statement(parser_t *parser)
{ {
if (parser->tok == TOKEN_TYPENAME || parser->tok == '.') ast_value *istype = NULL;
if (parser->tok == TOKEN_IDENT)
istype = parser_find_typedef(parser, parser_tokval(parser));
if (istype || parser->tok == TOKEN_TYPENAME || parser->tok == '.')
{ {
return parse_variable(parser, NULL, false, false); return parse_variable(parser, NULL, false, false, istype);
} }
else if (parser->tok == TOKEN_IDENT && !strcmp(parser_tokval(parser), "var")) else if (parser->tok == TOKEN_IDENT && !strcmp(parser_tokval(parser), "var"))
{ {
@ -3765,7 +3852,7 @@ static bool parser_global_statement(parser_t *parser)
parseerror(parser, "expected variable declaration after 'var'"); parseerror(parser, "expected variable declaration after 'var'");
return false; return false;
} }
return parse_variable(parser, NULL, true, false); return parse_variable(parser, NULL, true, false, NULL);
} }
} }
else if (parser->tok == TOKEN_KEYWORD) else if (parser->tok == TOKEN_KEYWORD)
@ -3775,7 +3862,14 @@ static bool parser_global_statement(parser_t *parser)
parseerror(parser, "expected variable declaration after 'const'"); parseerror(parser, "expected variable declaration after 'const'");
return false; return false;
} }
return parse_variable(parser, NULL, true, true); return parse_variable(parser, NULL, true, true, NULL);
}
else if (!strcmp(parser_tokval(parser), "typedef")) {
if (!parser_next(parser)) {
parseerror(parser, "expected type definition after 'typedef'");
return false;
}
return parse_typedef(parser);
} }
parseerror(parser, "unrecognized keyword `%s`", parser_tokval(parser)); parseerror(parser, "unrecognized keyword `%s`", parser_tokval(parser));
return false; return false;
@ -3897,6 +3991,7 @@ bool parser_init()
vec_push(parser->variables, parser->htfields = util_htnew(PARSER_HT_SIZE)); vec_push(parser->variables, parser->htfields = util_htnew(PARSER_HT_SIZE));
vec_push(parser->variables, parser->htglobals = util_htnew(PARSER_HT_SIZE)); vec_push(parser->variables, parser->htglobals = util_htnew(PARSER_HT_SIZE));
vec_push(parser->typedefs, util_htnew(TYPEDEF_HT_SIZE));
return true; return true;
} }
@ -3999,6 +4094,15 @@ void parser_cleanup()
for (i = 0; i < vec_size(parser->variables); ++i) for (i = 0; i < vec_size(parser->variables); ++i)
util_htdel(parser->variables[i]); util_htdel(parser->variables[i]);
vec_free(parser->variables); vec_free(parser->variables);
vec_free(parser->_blocklocals);
for (i = 0; i < vec_size(parser->_typedefs); ++i)
ast_delete(parser->_typedefs[i]);
vec_free(parser->_typedefs);
for (i = 0; i < vec_size(parser->typedefs); ++i)
util_htdel(parser->typedefs[i]);
vec_free(parser->typedefs);
vec_free(parser->_blocktypedefs);
mem_d(parser); mem_d(parser);
} }