X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=parser.c;h=5cbdb16d80c508d8021727d3650006b78b85f1e3;hb=2c0a9d78df46286eb566a1e0fa06e4cfc7605ad2;hp=41a87302a1721382a592ec084832cc9c3842099c;hpb=86f3ef2194877065124b1330a372d6f03af4dfeb;p=xonotic%2Fgmqcc.git diff --git a/parser.c b/parser.c index 41a8730..5cbdb16 100644 --- a/parser.c +++ b/parser.c @@ -1,24 +1,50 @@ +/* + * Copyright (C) 2012 + * Wolfgang Bumiller + * + * 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 substantial 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 #include #include "gmqcc.h" #include "lexer.h" -typedef struct { - char *name; - ast_expression *var; -} varentry_t; +#define PARSER_HT_FIELDS 0 +#define PARSER_HT_GLOBALS 1 +/* beginning of locals */ +#define PARSER_HT_LOCALS 2 + +#define PARSER_HT_SIZE 1024 +#define TYPEDEF_HT_SIZE 16 typedef struct { lex_file *lex; int tok; - varentry_t *globals; - varentry_t *fields; + ast_expression **globals; + ast_expression **fields; ast_function **functions; ast_value **imm_float; ast_value **imm_string; ast_value **imm_vector; + size_t translated; /* must be deleted first, they reference immediates and values */ ast_value **accessors; @@ -31,8 +57,18 @@ typedef struct { size_t crc_fields; ast_function *function; - varentry_t *locals; - size_t blocklocal; + + /* A list of hashtables for each scope */ + 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; @@ -46,9 +82,11 @@ typedef struct { qcint memberof; } parser_t; - -static bool GMQCC_WARN parser_pop_local(parser_t *parser); -static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofields, bool is_const); +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_typedef(parser_t *parser); +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); @@ -204,7 +242,7 @@ static char *parser_strdup(const char *str) return util_strdup(str); } -static ast_value* parser_const_string(parser_t *parser, const char *str) +static ast_value* parser_const_string(parser_t *parser, const char *str, bool dotranslate) { size_t i; ast_value *out; @@ -212,7 +250,12 @@ static ast_value* parser_const_string(parser_t *parser, const char *str) if (!strcmp(parser->imm_string[i]->constval.vstring, str)) return parser->imm_string[i]; } - out = ast_value_new(parser_ctx(parser), "#IMMEDIATE", TYPE_STRING); + if (dotranslate) { + char name[32]; + snprintf(name, sizeof(name), "dotranslate_%lu", (unsigned long)(parser->translated++)); + out = ast_value_new(parser_ctx(parser), name, TYPE_STRING); + } else + out = ast_value_new(parser_ctx(parser), "#IMMEDIATE", TYPE_STRING); out->isconst = true; out->constval.vstring = parser_strdup(str); vec_push(parser->imm_string, out); @@ -252,22 +295,12 @@ static ast_value* parser_const_vector_0(parser_t *parser) static ast_expression* parser_find_field(parser_t *parser, const char *name) { - size_t i; - for (i = 0; i < vec_size(parser->fields); ++i) { - if (!strcmp(parser->fields[i].name, name)) - return parser->fields[i].var; - } - return NULL; + return util_htget(parser->htfields, name); } static ast_expression* parser_find_global(parser_t *parser, const char *name) { - size_t i; - for (i = 0; i < vec_size(parser->globals); ++i) { - if (!strcmp(parser->globals[i].name, name)) - return parser->globals[i].var; - } - return NULL; + return util_htget(parser->htglobals, name); } static ast_expression* parser_find_param(parser_t *parser, const char *name) @@ -286,12 +319,16 @@ static ast_expression* parser_find_param(parser_t *parser, const char *name) static ast_expression* parser_find_local(parser_t *parser, const char *name, size_t upto, bool *isparam) { - size_t i; + size_t i, hash; + ast_expression *e; + + hash = util_hthash(parser->htglobals, name); + *isparam = false; - for (i = vec_size(parser->locals); i > upto;) { + for (i = vec_size(parser->variables); i > upto;) { --i; - if (!strcmp(parser->locals[i].name, name)) - return parser->locals[i].var; + if ( (e = util_htgeth(parser->variables[i], name, hash)) ) + return e; } *isparam = true; return parser_find_param(parser, name); @@ -306,6 +343,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 upto) +{ + size_t i, hash; + ast_value *e; + hash = util_hthash(parser->typedefs[0], name); + + for (i = vec_size(parser->typedefs); i > upto;) { + --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 */ @@ -1253,7 +1304,40 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma else parser->memberof = 0; - if (parser->tok == TOKEN_IDENT) + if (OPTS_FLAG(TRANSLATABLE_STRINGS) && + parser->tok == TOKEN_IDENT && !strcmp(parser_tokval(parser), "_")) + { + /* a translatable string */ + ast_value *val; + + if (wantop) { + parseerror(parser, "expected operator or end of statement, got constant"); + goto onerr; + } + + parser->lex->flags.noops = true; + if (!parser_next(parser) || parser->tok != '(') { + parseerror(parser, "use _(\"string\") to create a translatable string constant"); + goto onerr; + } + parser->lex->flags.noops = false; + if (!parser_next(parser) || parser->tok != TOKEN_STRINGCONST) { + parseerror(parser, "expected a constant string in translatable-string extension"); + goto onerr; + } + val = parser_const_string(parser, parser_tokval(parser), true); + wantop = true; + if (!val) + return false; + vec_push(sy.out, syexp(parser_ctx(parser), (ast_expression*)val)); + DEBUGSHUNTDO(con_out("push string\n")); + + if (!parser_next(parser) || parser->tok != ')') { + parseerror(parser, "expected closing paren after translatable string"); + goto onerr; + } + } + else if (parser->tok == TOKEN_IDENT) { ast_expression *var; if (wantop) { @@ -1328,7 +1412,7 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma goto onerr; } wantop = true; - val = parser_const_string(parser, parser_tokval(parser)); + val = parser_const_string(parser, parser_tokval(parser), false); if (!val) return false; vec_push(sy.out, syexp(parser_ctx(parser), (ast_expression*)val)); @@ -1546,6 +1630,60 @@ static ast_expression* parse_expression(parser_t *parser, bool stopatcomma) return e; } +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, typedefs; + + if (vec_size(parser->variables) <= PARSER_HT_LOCALS) { + parseerror(parser, "internal error: parser_leaveblock with no block"); + return false; + } + + util_htdel(vec_last(parser->variables)); + vec_pop(parser->variables); + if (!vec_size(parser->_blocklocals)) { + parseerror(parser, "internal error: parser_leaveblock with no block (2)"); + return false; + } + + locals = vec_last(parser->_blocklocals); + vec_pop(parser->_blocklocals); + while (vec_size(parser->_locals) != locals) { + ast_expression *e = vec_last(parser->_locals); + ast_value *v = (ast_value*)e; + vec_pop(parser->_locals); + if (ast_istype(e, ast_value) && !v->uses) { + if (parsewarning(parser, WARN_UNUSED_VARIABLE, "unused variable: `%s`", v->name)) + rv = false; + } + } + + 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; +} + +static void parser_addlocal(parser_t *parser, const char *name, ast_expression *e) +{ + vec_push(parser->_locals, e); + util_htset(vec_last(parser->variables), name, (void*)e); +} + static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out) { ast_ifthen *ifthen; @@ -1741,15 +1879,14 @@ 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) { - ast_loop *aloop; + ast_loop *aloop; ast_expression *initexpr, *cond, *increment, *ontrue; - size_t oldblocklocal; + ast_value *typevar; bool retval = true; lex_ctx ctx = parser_ctx(parser); - oldblocklocal = parser->blocklocal; - parser->blocklocal = vec_size(parser->locals); + parser_enterblock(parser); initexpr = NULL; cond = NULL; @@ -1767,7 +1904,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), 0); + + 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")) @@ -1776,7 +1917,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 != ';') @@ -1845,17 +1986,14 @@ static bool parse_for(parser_t *parser, ast_block *block, ast_expression **out) aloop = ast_loop_new(ctx, initexpr, cond, NULL, increment, ontrue); *out = (ast_expression*)aloop; - while (vec_size(parser->locals) > parser->blocklocal) - retval = retval && parser_pop_local(parser); - parser->blocklocal = oldblocklocal; + if (!parser_leaveblock(parser)) + retval = false; return retval; onerr: if (initexpr) ast_delete(initexpr); if (cond) ast_delete(cond); if (increment) ast_delete(increment); - while (vec_size(parser->locals) > parser->blocklocal) - (void)!parser_pop_local(parser); - parser->blocklocal = oldblocklocal; + (void)!parser_leaveblock(parser); return false; } @@ -2068,7 +2206,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), 0); + + if (typevar || parser->tok == TOKEN_TYPENAME || parser->tok == '.') { /* local variable */ if (!block) { @@ -2079,7 +2221,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; @@ -2096,7 +2238,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; @@ -2146,6 +2288,14 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression * } return true; } + 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, "Unexpected keyword"); return false; } @@ -2175,30 +2325,11 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression * } } -static bool GMQCC_WARN parser_pop_local(parser_t *parser) -{ - bool rv = true; - varentry_t *ve; - - ve = &vec_last(parser->locals); - if (!parser->errors) { - if (ast_istype(ve->var, ast_value) && !(((ast_value*)(ve->var))->uses)) { - if (parsewarning(parser, WARN_UNUSED_VARIABLE, "unused variable: `%s`", ve->name)) - rv = false; - } - } - mem_d(ve->name); - vec_pop(parser->locals); - return rv; -} - static bool parse_block_into(parser_t *parser, ast_block *block, bool warnreturn) { - size_t oldblocklocal; bool retval = true; - oldblocklocal = parser->blocklocal; - parser->blocklocal = vec_size(parser->locals); + parser_enterblock(parser); if (!parser_next(parser)) { /* skip the '{' */ parseerror(parser, "expected function body"); @@ -2207,7 +2338,7 @@ static bool parse_block_into(parser_t *parser, ast_block *block, bool warnreturn while (parser->tok != TOKEN_EOF && parser->tok < TOKEN_ERROR) { - ast_expression *expr; + ast_expression *expr = NULL; if (parser->tok == '}') break; @@ -2239,10 +2370,9 @@ static bool parse_block_into(parser_t *parser, ast_block *block, bool warnreturn } cleanup: - while (vec_size(parser->locals) > parser->blocklocal) - retval = retval && parser_pop_local(parser); - parser->blocklocal = oldblocklocal; - return !!block; + if (!parser_leaveblock(parser)) + retval = false; + return retval && !!block; } static ast_block* parse_block(parser_t *parser, bool warnreturn) @@ -2268,39 +2398,27 @@ static ast_expression* parse_statement_or_block(parser_t *parser) return expr; } -/* loop method */ -static bool create_vector_members(ast_value *var, varentry_t *ve) +static bool create_vector_members(ast_value *var, ast_member **me) { size_t i; size_t len = strlen(var->name); for (i = 0; i < 3; ++i) { - ve[i].var = (ast_expression*)ast_member_new(ast_ctx(var), (ast_expression*)var, i); - if (!ve[i].var) - break; - - ve[i].name = (char*)mem_a(len+3); - if (!ve[i].name) { - ast_delete(ve[i].var); + char *name = mem_a(len+3); + memcpy(name, var->name, len); + name[len+0] = '_'; + name[len+1] = 'x'+i; + name[len+2] = 0; + me[i] = ast_member_new(ast_ctx(var), (ast_expression*)var, i, name); + mem_d(name); + if (!me[i]) break; - } - - memcpy(ve[i].name, var->name, len); - ve[i].name[len] = '_'; - ve[i].name[len+1] = 'x'+i; - ve[i].name[len+2] = 0; } if (i == 3) return true; /* unroll */ - do { - --i; - mem_d(ve[i].name); - ast_delete(ve[i].var); - ve[i].name = NULL; - ve[i].var = NULL; - } while (i); + do { ast_member_delete(me[--i]); } while(i); return false; } @@ -2386,7 +2504,6 @@ static bool parse_function_body(parser_t *parser, ast_value *var) { /* qc allows the use of not-yet-declared functions here * - this automatically creates a prototype */ - varentry_t varent; ast_value *thinkfunc; ast_expression *functype = fld_think->expression.next; @@ -2403,9 +2520,8 @@ static bool parse_function_body(parser_t *parser, ast_value *var) return false; } - varent.var = (ast_expression*)thinkfunc; - varent.name = util_strdup(thinkfunc->name); - vec_push(parser->globals, varent); + vec_push(parser->globals, (ast_expression*)thinkfunc); + util_htset(parser->htglobals, thinkfunc->name, thinkfunc); nextthink = (ast_expression*)thinkfunc; } else { @@ -2519,10 +2635,12 @@ static bool parse_function_body(parser_t *parser, ast_value *var) } } + parser_enterblock(parser); + for (parami = 0; parami < vec_size(var->expression.params); ++parami) { size_t e; - varentry_t ve[3]; ast_value *param = var->expression.params[parami]; + ast_member *me[3]; if (param->expression.vtype != TYPE_VECTOR && (param->expression.vtype != TYPE_FIELD || @@ -2531,15 +2649,14 @@ static bool parse_function_body(parser_t *parser, ast_value *var) continue; } - if (!create_vector_members(param, ve)) { + if (!create_vector_members(param, me)) { ast_block_delete(block); return false; } for (e = 0; e < 3; ++e) { - vec_push(parser->locals, ve[e]); - ast_block_collect(block, ve[e].var); - ve[e].var = NULL; /* collected */ + parser_addlocal(parser, me[e]->name, (ast_expression*)me[e]); + ast_block_collect(block, (ast_expression*)me[e]); } } @@ -2560,8 +2677,12 @@ static bool parse_function_body(parser_t *parser, ast_value *var) vec_push(func->blocks, block); parser->function = old; - while (vec_size(parser->locals)) - retval = retval && parser_pop_local(parser); + if (!parser_leaveblock(parser)) + retval = false; + if (vec_size(parser->variables) != PARSER_HT_LOCALS) { + parseerror(parser, "internal error: local scopes left"); + retval = false; + } if (parser->tok == ';') return parser_next(parser); @@ -2575,10 +2696,7 @@ enderrfn: var->constval.vfunc = NULL; enderr: - while (vec_size(parser->locals)) { - mem_d(vec_last(parser->locals).name); - vec_pop(parser->locals); - } + (void)!parser_leaveblock(parser); parser->function = old; return false; } @@ -2950,7 +3068,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; @@ -3001,7 +3119,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); @@ -3111,7 +3229,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; @@ -3119,6 +3237,7 @@ static ast_value *parse_typename(parser_t *parser, ast_value **storebase) const char *name = NULL; bool isfield = false; bool wasarray = false; + size_t morefields = 0; ctx = parser_ctx(parser); @@ -3130,14 +3249,39 @@ 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) { + + /* Further dots are handled seperately because they won't be part of the + * basetype + */ + while (parser->tok == '.') { + ++morefields; + if (!parser_next(parser)) { + parseerror(parser, "expected typename for field definition"); + return NULL; + } + } + + if (parser->tok == TOKEN_IDENT) + cached_typedef = parser_find_typedef(parser, parser_tokval(parser), 0); + if (!cached_typedef && parser->tok != TOKEN_TYPENAME) { parseerror(parser, "expected typename"); return NULL; } } /* generate the basic type value */ - var = ast_value_new(ctx, "", parser_token(parser)->constval.t); + if (cached_typedef) { + var = ast_value_copy(cached_typedef); + ast_value_set_name(var, ""); + } else + var = ast_value_new(ctx, "", parser_token(parser)->constval.t); + + for (; morefields; --morefields) { + tmp = ast_value_new(ctx, "<.type>", TYPE_FIELD); + tmp->expression.next = (ast_expression*)var; + var = tmp; + } + /* do not yet turn into a field - remember: * .void() foo; is a field too * .void()() foo; is a function @@ -3227,7 +3371,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, vec_last(parser->_blocktypedefs))) ) { + 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; @@ -3242,19 +3426,16 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield bool cleanvar = true; bool wasarray = false; - varentry_t varent, ve[3]; + 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); return false; } - memset(&varent, 0, sizeof(varent)); - memset(&ve, 0, sizeof(ve)); - while (true) { proto = NULL; wasarray = false; @@ -3409,7 +3590,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield } else /* it's not a global */ { - old = parser_find_local(parser, var->name, parser->blocklocal, &isparam); + old = parser_find_local(parser, var->name, vec_size(parser->variables)-1, &isparam); if (old && !isparam) { parseerror(parser, "local `%s` already declared here: %s:%i", var->name, ast_ctx(old).file, (int)ast_ctx(old).line); @@ -3448,48 +3629,48 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield isvector = true; if (isvector) { - if (!create_vector_members(var, ve)) { + if (!create_vector_members(var, me)) { retval = false; goto cleanup; } } - varent.name = util_strdup(var->name); - varent.var = (ast_expression*)var; - if (!localblock) { /* deal with global variables, fields, functions */ if (!nofields && var->expression.vtype == TYPE_FIELD) { - vec_push(parser->fields, varent); + vec_push(parser->fields, (ast_expression*)var); + util_htset(parser->htfields, var->name, var); if (isvector) { - for (i = 0; i < 3; ++i) - vec_push(parser->fields, ve[i]); + for (i = 0; i < 3; ++i) { + vec_push(parser->fields, (ast_expression*)me[i]); + util_htset(parser->htfields, me[i]->name, me[i]); + } } } else { - vec_push(parser->globals, varent); + vec_push(parser->globals, (ast_expression*)var); + util_htset(parser->htglobals, var->name, var); if (isvector) { - for (i = 0; i < 3; ++i) - vec_push(parser->globals, ve[i]); + for (i = 0; i < 3; ++i) { + vec_push(parser->globals, (ast_expression*)me[i]); + util_htset(parser->htglobals, me[i]->name, me[i]); + } } } } else { - vec_push(parser->locals, varent); vec_push(localblock->locals, var); + parser_addlocal(parser, var->name, (ast_expression*)var); if (isvector) { for (i = 0; i < 3; ++i) { - vec_push(parser->locals, ve[i]); - ast_block_collect(localblock, ve[i].var); - ve[i].var = NULL; /* from here it's being collected in the block */ + parser_addlocal(parser, me[i]->name, (ast_expression*)me[i]); + ast_block_collect(localblock, (ast_expression*)me[i]); } } } - varent.name = NULL; - ve[0].name = ve[1].name = ve[2].name = NULL; - ve[0].var = ve[1].var = ve[2].var = NULL; - cleanvar = false; } + me[0] = me[1] = me[2] = NULL; + cleanvar = false; /* Part 2.2 * deal with arrays */ @@ -3560,7 +3741,7 @@ skipvar: if (parser->tok != '{') { if (parser->tok != '=') { - parseerror(parser, "missing semicolon or initializer"); + parseerror(parser, "missing semicolon or initializer, got: `%s`", parser_tokval(parser)); break; } @@ -3574,7 +3755,7 @@ skipvar: } if (parser->tok == '#') { - ast_function *func; + ast_function *func = NULL; if (localblock) { parseerror(parser, "cannot declare builtins within functions"); @@ -3617,7 +3798,8 @@ skipvar: if (!parser_next(parser)) { parseerror(parser, "expected comma or semicolon"); - ast_function_delete(func); + if (func) + ast_function_delete(func); var->constval.vfunc = NULL; break; } @@ -3715,21 +3897,21 @@ cleanup: ast_delete(basetype); if (cleanvar && var) ast_delete(var); - if (varent.name) mem_d(varent.name); - if (ve[0].name) mem_d(ve[0].name); - if (ve[1].name) mem_d(ve[1].name); - if (ve[2].name) mem_d(ve[2].name); - if (ve[0].var) mem_d(ve[0].var); - if (ve[1].var) mem_d(ve[1].var); - if (ve[2].var) mem_d(ve[2].var); + if (me[0]) ast_member_delete(me[0]); + if (me[1]) ast_member_delete(me[1]); + if (me[2]) ast_member_delete(me[2]); return retval; } 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), 0); + + 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")) { @@ -3738,7 +3920,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) @@ -3748,7 +3930,21 @@ static bool parser_global_statement(parser_t *parser) parseerror(parser, "expected variable declaration after 'const'"); return false; } - return parse_variable(parser, NULL, true, true); + if (parser->tok == TOKEN_IDENT && !strcmp(parser_tokval(parser), "var")) { + (void)!parsewarning(parser, WARN_CONST_VAR, "ignoring `var` after const qualifier"); + if (!parser_next(parser)) { + parseerror(parser, "expected variable declaration after 'const var'"); + return false; + } + } + 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; @@ -3788,8 +3984,9 @@ static uint16_t progdefs_crc_both(uint16_t old, const char *str) static void generate_checksum(parser_t *parser) { - uint16_t crc = 0xFFFF; - size_t i; + uint16_t crc = 0xFFFF; + size_t i; + ast_value *value; crc = progdefs_crc_both(crc, "\n/* file generated by qcc, do not modify */\n\ntypedef struct\n{"); crc = progdefs_crc_sum(crc, "\tint\tpad[28];\n"); @@ -3806,9 +4003,10 @@ static void generate_checksum(parser_t *parser) progdefs_crc_file("\tint\tofs_parm7[3];\n"); */ for (i = 0; i < parser->crc_globals; ++i) { - if (!ast_istype(parser->globals[i].var, ast_value)) + if (!ast_istype(parser->globals[i], ast_value)) continue; - switch (parser->globals[i].var->expression.vtype) { + value = (ast_value*)(parser->globals[i]); + switch (value->expression.vtype) { case TYPE_FLOAT: crc = progdefs_crc_both(crc, "\tfloat\t"); break; case TYPE_VECTOR: crc = progdefs_crc_both(crc, "\tvec3_t\t"); break; case TYPE_STRING: crc = progdefs_crc_both(crc, "\tstring_t\t"); break; @@ -3817,14 +4015,15 @@ static void generate_checksum(parser_t *parser) crc = progdefs_crc_both(crc, "\tint\t"); break; } - crc = progdefs_crc_both(crc, parser->globals[i].name); + crc = progdefs_crc_both(crc, value->name); crc = progdefs_crc_both(crc, ";\n"); } crc = progdefs_crc_both(crc, "} globalvars_t;\n\ntypedef struct\n{\n"); for (i = 0; i < parser->crc_fields; ++i) { - if (!ast_istype(parser->fields[i].var, ast_value)) + if (!ast_istype(parser->fields[i], ast_value)) continue; - switch (parser->fields[i].var->expression.next->expression.vtype) { + value = (ast_value*)(parser->fields[i]); + switch (value->expression.next->expression.vtype) { case TYPE_FLOAT: crc = progdefs_crc_both(crc, "\tfloat\t"); break; case TYPE_VECTOR: crc = progdefs_crc_both(crc, "\tvec3_t\t"); break; case TYPE_STRING: crc = progdefs_crc_both(crc, "\tstring_t\t"); break; @@ -3833,7 +4032,7 @@ static void generate_checksum(parser_t *parser) crc = progdefs_crc_both(crc, "\tint\t"); break; } - crc = progdefs_crc_both(crc, parser->fields[i].name); + crc = progdefs_crc_both(crc, value->name); crc = progdefs_crc_both(crc, ";\n"); } crc = progdefs_crc_both(crc, "} entvars_t;\n\n"); @@ -3846,6 +4045,7 @@ static parser_t *parser; bool parser_init() { size_t i; + parser = (parser_t*)mem_a(sizeof(parser_t)); if (!parser) return false; @@ -3863,6 +4063,11 @@ bool parser_init() mem_d(parser); return false; } + + 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)); + vec_push(parser->_blocktypedefs, 0); return true; } @@ -3949,12 +4154,10 @@ void parser_cleanup() ast_delete(parser->imm_float[i]); } for (i = 0; i < vec_size(parser->fields); ++i) { - ast_delete(parser->fields[i].var); - mem_d(parser->fields[i].name); + ast_delete(parser->fields[i]); } for (i = 0; i < vec_size(parser->globals); ++i) { - ast_delete(parser->globals[i].var); - mem_d(parser->globals[i].name); + ast_delete(parser->globals[i]); } vec_free(parser->accessors); vec_free(parser->functions); @@ -3963,7 +4166,20 @@ void parser_cleanup() vec_free(parser->imm_float); vec_free(parser->globals); vec_free(parser->fields); - vec_free(parser->locals); + + for (i = 0; i < vec_size(parser->variables); ++i) + util_htdel(parser->variables[i]); + vec_free(parser->variables); + vec_free(parser->_blocklocals); + vec_free(parser->_locals); + + 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); } @@ -3985,9 +4201,9 @@ bool parser_finish(const char *output) for (i = 0; i < vec_size(parser->fields); ++i) { ast_value *field; bool isconst; - if (!ast_istype(parser->fields[i].var, ast_value)) + if (!ast_istype(parser->fields[i], ast_value)) continue; - field = (ast_value*)parser->fields[i].var; + field = (ast_value*)parser->fields[i]; isconst = field->isconst; field->isconst = false; if (!ast_global_codegen((ast_value*)field, ir, true)) { @@ -4010,9 +4226,9 @@ bool parser_finish(const char *output) } for (i = 0; i < vec_size(parser->globals); ++i) { ast_value *asvalue; - if (!ast_istype(parser->globals[i].var, ast_value)) + if (!ast_istype(parser->globals[i], ast_value)) continue; - asvalue = (ast_value*)(parser->globals[i].var); + asvalue = (ast_value*)(parser->globals[i]); if (!asvalue->uses && !asvalue->isconst && asvalue->expression.vtype != TYPE_FUNCTION) { if (strcmp(asvalue->name, "end_sys_globals") && strcmp(asvalue->name, "end_sys_fields")) @@ -4022,7 +4238,7 @@ bool parser_finish(const char *output) } } if (!ast_global_codegen(asvalue, ir, false)) { - con_out("failed to generate global %s\n", parser->globals[i].name); + con_out("failed to generate global %s\n", asvalue->name); ir_builder_delete(ir); return false; } @@ -4050,15 +4266,15 @@ bool parser_finish(const char *output) } for (i = 0; i < vec_size(parser->globals); ++i) { ast_value *asvalue; - if (!ast_istype(parser->globals[i].var, ast_value)) + if (!ast_istype(parser->globals[i], ast_value)) continue; - asvalue = (ast_value*)(parser->globals[i].var); + asvalue = (ast_value*)(parser->globals[i]); if (asvalue->setter) { if (!ast_global_codegen(asvalue->setter, ir, false) || !ast_function_codegen(asvalue->setter->constval.vfunc, ir) || !ir_function_finalize(asvalue->setter->constval.vfunc->ir_func)) { - printf("failed to generate setter for %s\n", parser->globals[i].name); + printf("failed to generate setter for %s\n", asvalue->name); ir_builder_delete(ir); return false; } @@ -4068,7 +4284,7 @@ bool parser_finish(const char *output) !ast_function_codegen(asvalue->getter->constval.vfunc, ir) || !ir_function_finalize(asvalue->getter->constval.vfunc->ir_func)) { - printf("failed to generate getter for %s\n", parser->globals[i].name); + printf("failed to generate getter for %s\n", asvalue->name); ir_builder_delete(ir); return false; } @@ -4076,7 +4292,7 @@ bool parser_finish(const char *output) } for (i = 0; i < vec_size(parser->fields); ++i) { ast_value *asvalue; - asvalue = (ast_value*)(parser->fields[i].var->expression.next); + asvalue = (ast_value*)(parser->fields[i]->expression.next); if (!ast_istype((ast_expression*)asvalue, ast_value)) continue; @@ -4087,7 +4303,7 @@ bool parser_finish(const char *output) !ast_function_codegen(asvalue->setter->constval.vfunc, ir) || !ir_function_finalize(asvalue->setter->constval.vfunc->ir_func)) { - printf("failed to generate setter for %s\n", parser->fields[i].name); + printf("failed to generate setter for %s\n", asvalue->name); ir_builder_delete(ir); return false; } @@ -4097,7 +4313,7 @@ bool parser_finish(const char *output) !ast_function_codegen(asvalue->getter->constval.vfunc, ir) || !ir_function_finalize(asvalue->getter->constval.vfunc->ir_func)) { - printf("failed to generate getter for %s\n", parser->fields[i].name); + printf("failed to generate getter for %s\n", asvalue->name); ir_builder_delete(ir); return false; }