X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=parser.c;h=5cbdb16d80c508d8021727d3650006b78b85f1e3;hb=2c0a9d78df46286eb566a1e0fa06e4cfc7605ad2;hp=1b6cc09968f1d8e38525a4aba32d2a64d6615753;hpb=d69b576d6b9a8a5c09ca3c5c8f2b262b67b6aa57;p=xonotic%2Fgmqcc.git diff --git a/parser.c b/parser.c index 1b6cc09..5cbdb16 100644 --- a/parser.c +++ b/parser.c @@ -32,6 +32,7 @@ #define PARSER_HT_LOCALS 2 #define PARSER_HT_SIZE 1024 +#define TYPEDEF_HT_SIZE 16 typedef struct { lex_file *lex; @@ -43,6 +44,7 @@ typedef struct { 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; @@ -60,10 +62,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 +85,8 @@ 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_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); @@ -236,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; @@ -244,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); @@ -332,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 */ @@ -1279,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) { @@ -1354,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)); @@ -1576,12 +1634,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 +1667,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; } @@ -1811,8 +1879,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) { - ast_loop *aloop; + ast_loop *aloop; ast_expression *initexpr, *cond, *increment, *ontrue; + ast_value *typevar; bool retval = true; lex_ctx ctx = parser_ctx(parser); @@ -1835,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")) @@ -1844,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 != ';') @@ -2133,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) { @@ -2144,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; @@ -2161,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; @@ -2211,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; } @@ -2253,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; @@ -2983,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; @@ -3034,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); @@ -3144,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; @@ -3152,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); @@ -3163,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 @@ -3260,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; @@ -3278,7 +3429,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); @@ -3517,9 +3668,9 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield } } - me[0] = me[1] = me[2] = NULL; - cleanvar = false; } + me[0] = me[1] = me[2] = NULL; + cleanvar = false; /* Part 2.2 * deal with arrays */ @@ -3590,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; } @@ -3754,9 +3905,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), 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")) { @@ -3765,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) @@ -3775,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; @@ -3897,6 +4066,8 @@ 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)); + vec_push(parser->_blocktypedefs, 0); return true; } @@ -3999,6 +4170,16 @@ 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); + 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); }