X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=parser.c;h=4d50bad5e15232cc0da981bd08484b3362b131f8;hb=a0fa90ddd555f7d55042b0fb542e63e6b10ad7e2;hp=b3e98823604b553363ff9db41caa7151d1551e51;hpb=685fa54daf0b3fdd92d9424e7fcab1ded9fb3c29;p=xonotic%2Fgmqcc.git diff --git a/parser.c b/parser.c index b3e9882..4d50bad 100644 --- a/parser.c +++ b/parser.c @@ -103,6 +103,9 @@ typedef struct parser_s { /* collected information */ size_t max_param_count; + + /* code generator */ + code_t *code; } parser_t; static ast_expression * const intrinsic_debug_typestring = (ast_expression*)0x1; @@ -217,6 +220,7 @@ static ast_value* parser_const_float(parser_t *parser, double d) out = ast_value_new(ctx, "#IMMEDIATE", TYPE_FLOAT); out->cvq = CV_CONST; out->hasvalue = true; + out->isimm = true; out->constval.vfloat = d; vec_push(parser->imm_float, out); return out; @@ -257,10 +261,10 @@ static ast_value* parser_const_string(parser_t *parser, const char *str, bool do { size_t hash = util_hthash(parser->ht_imm_string, str); ast_value *out; - if ( (out = util_htgeth(parser->ht_imm_string, str, hash)) ) { + if ( (out = (ast_value*)util_htgeth(parser->ht_imm_string, str, hash)) ) { if (dotranslate && out->name[0] == '#') { char name[32]; - snprintf(name, sizeof(name), "dotranslate_%lu", (unsigned long)(parser->translated++)); + util_snprintf(name, sizeof(name), "dotranslate_%lu", (unsigned long)(parser->translated++)); ast_value_set_name(out, name); } return out; @@ -273,12 +277,13 @@ static ast_value* parser_const_string(parser_t *parser, const char *str, bool do */ if (dotranslate) { char name[32]; - snprintf(name, sizeof(name), "dotranslate_%lu", (unsigned long)(parser->translated++)); + util_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->cvq = CV_CONST; out->hasvalue = true; + out->isimm = true; out->constval.vstring = parser_strdup(str); vec_push(parser->imm_string, out); util_htseth(parser->ht_imm_string, str, hash, out); @@ -296,6 +301,7 @@ static ast_value* parser_const_vector(parser_t *parser, vector v) out = ast_value_new(parser_ctx(parser), "#IMMEDIATE", TYPE_VECTOR); out->cvq = CV_CONST; out->hasvalue = true; + out->isimm = true; out->constval.vvec = v; vec_push(parser->imm_vector, out); return out; @@ -1782,6 +1788,9 @@ static ast_expression* parse_vararg(parser_t *parser) return out; } +/* not to be exposed */ +extern bool ftepp_predef_exists(const char *name); + static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels) { if (OPTS_FLAG(TRANSLATABLE_STRINGS) && @@ -1888,6 +1897,8 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels) vec_push(parser->labels, lbl); } } + if (!var && !strcmp(parser_tokval(parser), "__FUNC__")) + var = (ast_expression*)parser_const_string(parser, parser->function->name, false); if (!var) { /* intrinsics */ if (!strcmp(parser_tokval(parser), "__builtin_debug_typestring")) { @@ -1899,8 +1910,6 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels) */ else if (!strncmp(parser_tokval(parser), "__builtin_", 10)) { var = intrin_func(parser, parser_tokval(parser) + 10 /* skip __builtin */); - } else { - var = intrin_func(parser, parser_tokval(parser)); } if (!var) { @@ -1912,13 +1921,9 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels) * i've done this thousands of times already myself. Lets check for * it in the predef table. And diagnose it better :) */ - if (!OPTS_FLAG(FTEPP_PREDEFS)) { - for (i = 0; i < sizeof(ftepp_predefs)/sizeof(*ftepp_predefs); i++) { - if (!strcmp(ftepp_predefs[i].name, parser_tokval(parser))) { - parseerror(parser, "unexpected ident: %s (use -fftepp-predef to enable pre-defined macros)", parser_tokval(parser)); - return false; - } - } + if (!OPTS_FLAG(FTEPP_PREDEFS) && ftepp_predef_exists(parser_tokval(parser))) { + parseerror(parser, "unexpected ident: %s (use -fftepp-predef to enable pre-defined macros)", parser_tokval(parser)); + return false; } /* @@ -2182,8 +2187,27 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma wantop = true; } else { - parseerror(parser, "expected operator or end of statement"); - goto onerr; + /* in this case we might want to allow constant string concatenation */ + bool concatenated = false; + if (parser->tok == TOKEN_STRINGCONST && vec_size(sy.out)) { + ast_expression *lexpr = vec_last(sy.out).out; + if (ast_istype(lexpr, ast_value)) { + ast_value *last = (ast_value*)lexpr; + if (last->isimm == true && last->cvq == CV_CONST && + last->hasvalue && last->expression.vtype == TYPE_STRING) + { + char *newstr = NULL; + util_asprintf(&newstr, "%s%s", last->constval.vstring, parser_tokval(parser)); + vec_last(sy.out).out = (ast_expression*)parser_const_string(parser, newstr, false); + mem_d(newstr); + concatenated = true; + } + } + } + if (!concatenated) { + parseerror(parser, "expected operator or end of statement"); + goto onerr; + } } if (!parser_next(parser)) { @@ -2203,8 +2227,8 @@ static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma } parser->lex->flags.noops = true; - if (!vec_size(sy.out)) { - parseerror(parser, "empty expression"); + if (vec_size(sy.out) != 1) { + parseerror(parser, "expression with not 1 but %lu output values...", (unsigned long) vec_size(sy.out)); expr = NULL; } else expr = sy.out[0].out; @@ -2429,17 +2453,17 @@ static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out) /* closing paren */ if (parser->tok != ')') { parseerror(parser, "expected closing paren after 'if' condition"); - ast_delete(cond); + ast_unref(cond); return false; } /* parse into the 'then' branch */ if (!parser_next(parser)) { parseerror(parser, "expected statement for on-true branch of 'if'"); - ast_delete(cond); + ast_unref(cond); return false; } if (!parse_statement_or_block(parser, &ontrue)) { - ast_delete(cond); + ast_unref(cond); return false; } if (!ontrue) @@ -2450,12 +2474,12 @@ static bool parse_if(parser_t *parser, ast_block *block, ast_expression **out) if (!parser_next(parser)) { parseerror(parser, "expected on-false branch after 'else'"); ast_delete(ontrue); - ast_delete(cond); + ast_unref(cond); return false; } if (!parse_statement_or_block(parser, &onfalse)) { ast_delete(ontrue); - ast_delete(cond); + ast_unref(cond); return false; } } @@ -2552,23 +2576,23 @@ static bool parse_while_go(parser_t *parser, ast_block *block, ast_expression ** /* closing paren */ if (parser->tok != ')') { parseerror(parser, "expected closing paren after 'while' condition"); - ast_delete(cond); + ast_unref(cond); return false; } /* parse into the 'then' branch */ if (!parser_next(parser)) { parseerror(parser, "expected while-loop body"); - ast_delete(cond); + ast_unref(cond); return false; } if (!parse_statement_or_block(parser, &ontrue)) { - ast_delete(cond); + ast_unref(cond); return false; } cond = process_condition(parser, cond, &ifnot); if (!cond) { - ast_delete(ontrue); + ast_unref(ontrue); return false; } aloop = ast_loop_new(ctx, NULL, cond, ifnot, NULL, false, NULL, ontrue); @@ -2668,21 +2692,21 @@ static bool parse_dowhile_go(parser_t *parser, ast_block *block, ast_expression if (parser->tok != ')') { parseerror(parser, "expected closing paren after 'while' condition"); ast_delete(ontrue); - ast_delete(cond); + ast_unref(cond); return false; } /* parse on */ if (!parser_next(parser) || parser->tok != ';') { parseerror(parser, "expected semicolon after condition"); ast_delete(ontrue); - ast_delete(cond); + ast_unref(cond); return false; } if (!parser_next(parser)) { parseerror(parser, "parse error"); ast_delete(ontrue); - ast_delete(cond); + ast_unref(cond); return false; } @@ -2755,7 +2779,6 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou ast_expression *initexpr, *cond, *increment, *ontrue; ast_value *typevar; - bool retval = true; bool ifnot = false; lex_ctx ctx = parser_ctx(parser); @@ -2855,13 +2878,15 @@ static bool parse_for_go(parser_t *parser, ast_block *block, ast_expression **ou aloop = ast_loop_new(ctx, initexpr, cond, ifnot, NULL, false, increment, ontrue); *out = (ast_expression*)aloop; - if (!parser_leaveblock(parser)) - retval = false; - return retval; + if (!parser_leaveblock(parser)) { + ast_delete(aloop); + return false; + } + return true; onerr: - if (initexpr) ast_delete(initexpr); - if (cond) ast_delete(cond); - if (increment) ast_delete(increment); + if (initexpr) ast_unref(initexpr); + if (cond) ast_unref(cond); + if (increment) ast_unref(increment); (void)!parser_leaveblock(parser); return false; } @@ -2894,7 +2919,7 @@ static bool parse_return(parser_t *parser, ast_block *block, ast_expression **ou ret = ast_return_new(ctx, exp); if (!ret) { - ast_delete(exp); + ast_unref(exp); return false; } } else { @@ -4227,13 +4252,13 @@ static bool parse_function_body(parser_t *parser, ast_value *var) varargs->expression.flags |= AST_FLAG_IS_VARARG; varargs->expression.next = (ast_expression*)ast_value_new(ast_ctx(var), NULL, TYPE_VECTOR); varargs->expression.count = 0; - snprintf(name, sizeof(name), "%s##va##SET", var->name); + util_snprintf(name, sizeof(name), "%s##va##SET", var->name); if (!parser_create_array_setter_proto(parser, varargs, name)) { ast_delete(varargs); ast_block_delete(block); goto enderrfn; } - snprintf(name, sizeof(name), "%s##va##GET", var->name); + util_snprintf(name, sizeof(name), "%s##va##GET", var->name); if (!parser_create_array_getter_proto(parser, varargs, varargs->expression.next, name)) { ast_delete(varargs); ast_block_delete(block); @@ -5114,7 +5139,7 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield bool cleanvar = true; bool wasarray = false; - ast_member *me[3]; + ast_member *me[3] = { NULL, NULL, NULL }; if (!localblock && is_static) parseerror(parser, "`static` qualifier is not supported in global scope"); @@ -5533,10 +5558,10 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield */ if (var->expression.vtype == TYPE_ARRAY) { char name[1024]; - snprintf(name, sizeof(name), "%s##SET", var->name); + util_snprintf(name, sizeof(name), "%s##SET", var->name); if (!parser_create_array_setter(parser, var, name)) goto cleanup; - snprintf(name, sizeof(name), "%s##GET", var->name); + util_snprintf(name, sizeof(name), "%s##GET", var->name); if (!parser_create_array_getter(parser, var, var->expression.next, name)) goto cleanup; } @@ -5554,14 +5579,14 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield goto cleanup; } - snprintf(name, sizeof(name), "%s##SETF", var->name); + util_snprintf(name, sizeof(name), "%s##SETF", var->name); if (!parser_create_array_field_setter(parser, array, name)) goto cleanup; telem = ast_type_copy(ast_ctx(var), array->expression.next); tfield = ast_value_new(ast_ctx(var), "<.type>", TYPE_FIELD); tfield->expression.next = telem; - snprintf(name, sizeof(name), "%s##GETFP", var->name); + util_snprintf(name, sizeof(name), "%s##GETFP", var->name); if (!parser_create_array_getter(parser, array, (ast_expression*)tfield, name)) { ast_delete(tfield); goto cleanup; @@ -5599,7 +5624,7 @@ skipvar: } } - if (parser->tok != '{') { + if (parser->tok != '{' || var->expression.vtype != TYPE_FUNCTION) { if (parser->tok != '=') { parseerror(parser, "missing semicolon or initializer, got: `%s`", parser_tokval(parser)); break; @@ -5615,7 +5640,11 @@ skipvar: } if (parser->tok == '#') { - ast_function *func = NULL; + ast_function *func = NULL; + ast_value *number = NULL; + float fractional; + float integral; + int builtin_num; if (localblock) { parseerror(parser, "cannot declare builtins within functions"); @@ -5629,12 +5658,42 @@ skipvar: parseerror(parser, "expected builtin number"); break; } - if (parser->tok != TOKEN_INTCONST) { - parseerror(parser, "builtin number must be an integer constant"); - break; - } - if (parser_token(parser)->constval.i < 0) { - parseerror(parser, "builtin number must be an integer greater than zero"); + + if (OPTS_FLAG(EXPRESSIONS_FOR_BUILTINS)) { + number = (ast_value*)parse_expression_leave(parser, true, false, false); + if (!number) { + parseerror(parser, "builtin number expected"); + break; + } + if (!ast_istype(number, ast_value) || !number->hasvalue || number->cvq != CV_CONST) + { + ast_unref(number); + parseerror(parser, "builtin number must be a compile time constant"); + break; + } + if (number->expression.vtype == TYPE_INTEGER) + builtin_num = number->constval.vint; + else if (number->expression.vtype == TYPE_FLOAT) + builtin_num = number->constval.vfloat; + else { + ast_unref(number); + parseerror(parser, "builtin number must be an integer constant"); + break; + } + ast_unref(number); + + fractional = modff(builtin_num, &integral); + if (builtin_num < 0 || fractional != 0) { + parseerror(parser, "builtin number must be an integer greater than zero"); + break; + } + + /* we only want the integral part anyways */ + builtin_num = integral; + } else if (parser->tok == TOKEN_INTCONST) { + builtin_num = parser_token(parser)->constval.i; + } else { + parseerror(parser, "builtin number must be a compile time constant"); break; } @@ -5653,10 +5712,13 @@ skipvar: } vec_push(parser->functions, func); - func->builtin = -parser_token(parser)->constval.i-1; + func->builtin = -builtin_num-1; } - if (!parser_next(parser)) { + if (OPTS_FLAG(EXPRESSIONS_FOR_BUILTINS) + ? (parser->tok != ',' && parser->tok != ';') + : (!parser_next(parser))) + { parseerror(parser, "expected comma or semicolon"); if (func) ast_function_delete(func); @@ -5664,7 +5726,22 @@ skipvar: break; } } - else if (parser->tok == '{' || parser->tok == '[') + else if (var->expression.vtype == TYPE_ARRAY && parser->tok == '{') + { + if (localblock) { + /* Note that fteqcc and most others don't even *have* + * local arrays, so this is not a high priority. + */ + parseerror(parser, "TODO: initializers for local arrays"); + break; + } + /* +static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue, bool with_labels); +*/ + parseerror(parser, "TODO: initializing global arrays is not supported yet!"); + break; + } + else if (var->expression.vtype == TYPE_FUNCTION && (parser->tok == '{' || parser->tok == '[')) { if (localblock) { parseerror(parser, "cannot declare functions within functions"); @@ -5921,7 +5998,7 @@ static void generate_checksum(parser_t *parser) } crc = progdefs_crc_both(crc, "} entvars_t;\n\n"); - code_crc = crc; + parser->code->crc = crc; } parser_t *parser_create() @@ -5936,6 +6013,11 @@ parser_t *parser_create() memset(parser, 0, sizeof(*parser)); + if (!(parser->code = code_init())) { + mem_d(parser); + return NULL; + } + for (i = 0; i < operator_count; ++i) { if (operators[i].id == opid1('=')) { parser->assign_op = operators+i; @@ -6113,6 +6195,8 @@ void parser_cleanup(parser_t *parser) intrin_intrinsics_destroy(parser); + code_cleanup(parser->code); + mem_d(parser); } @@ -6291,7 +6375,7 @@ bool parser_finish(parser_t *parser, const char *output) generate_checksum(parser); - if (!ir_builder_generate(ir, output)) { + if (!ir_builder_generate(parser->code, ir, output)) { con_out("*** failed to generate output file\n"); ir_builder_delete(ir); return false;