Implementing typedef

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

138
parser.c
View file

@ -32,6 +32,7 @@
#define PARSER_HT_LOCALS 2
#define PARSER_HT_SIZE 1024
#define TYPEDEF_HT_SIZE 16
typedef struct {
lex_file *lex;
@ -60,10 +61,13 @@ typedef struct {
ht *variables;
ht htfields;
ht htglobals;
ht *typedefs;
/* not to be used directly, we use the hash table */
ast_expression **_locals;
size_t *_blocklocals;
ast_value **_typedefs;
size_t *_blocktypedefs;
size_t errors;
@ -80,7 +84,7 @@ typedef struct {
static void parser_enterblock(parser_t *parser);
static bool parser_leaveblock(parser_t *parser);
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 bool parse_block_into(parser_t *parser, ast_block *block, bool warnreturn);
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;
}
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
{
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->_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)
{
bool rv = true;
size_t locals;
size_t locals, typedefs;
if (vec_size(parser->variables) <= PARSER_HT_LOCALS) {
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;
}
@ -1813,6 +1841,7 @@ static bool parse_for(parser_t *parser, ast_block *block, ast_expression **out)
{
ast_loop *aloop;
ast_expression *initexpr, *cond, *increment, *ontrue;
ast_value *typevar;
bool retval = true;
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;
}
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 (parsewarning(parser, WARN_EXTENSIONS,
"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");
goto onerr;
if (!parse_variable(parser, block, true, false))
if (!parse_variable(parser, block, true, false, typevar))
goto onerr;
}
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)
{
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 */
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"))
return false;
}
if (!parse_variable(parser, block, false, false))
if (!parse_variable(parser, block, false, false, typevar))
return false;
*out = NULL;
return true;
@ -2161,7 +2198,7 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
parseerror(parser, "expected variable declaration");
return false;
}
if (!parse_variable(parser, block, true, false))
if (!parse_variable(parser, block, true, false, NULL))
return false;
*out = NULL;
return true;
@ -2983,7 +3020,7 @@ cleanup:
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)
{
lex_ctx ctx;
@ -3034,7 +3071,7 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
else
{
/* for anything else just parse a typename */
param = parse_typename(parser, NULL);
param = parse_typename(parser, NULL, NULL);
if (!param)
goto on_error;
vec_push(params, param);
@ -3144,7 +3181,7 @@ static ast_value *parse_arraysize(parser_t *parser, ast_value *var)
* void() foo(), bar
* 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;
lex_ctx ctx;
@ -3163,13 +3200,19 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase)
parseerror(parser, "expected typename for field definition");
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");
return NULL;
}
}
/* generate the basic type value */
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:
* .void() foo; is a field too
@ -3260,7 +3303,47 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase)
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 *proto;
@ -3278,7 +3361,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
ast_member *me[3];
/* get the first complete variable */
var = parse_typename(parser, &basetype);
var = parse_typename(parser, &basetype, cached_typedef);
if (!var) {
if (basetype)
ast_delete(basetype);
@ -3754,9 +3837,13 @@ cleanup:
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"))
{
@ -3765,7 +3852,7 @@ static bool parser_global_statement(parser_t *parser)
parseerror(parser, "expected variable declaration after 'var'");
return false;
}
return parse_variable(parser, NULL, true, false);
return parse_variable(parser, NULL, true, false, NULL);
}
}
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'");
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));
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->htglobals = util_htnew(PARSER_HT_SIZE));
vec_push(parser->typedefs, util_htnew(TYPEDEF_HT_SIZE));
return true;
}
@ -3999,6 +4094,15 @@ void parser_cleanup()
for (i = 0; i < vec_size(parser->variables); ++i)
util_htdel(parser->variables[i]);
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);
}