diff --git a/engine/qclib/pr_comp.h b/engine/qclib/pr_comp.h index 1259592a..12d1f1bd 100644 --- a/engine/qclib/pr_comp.h +++ b/engine/qclib/pr_comp.h @@ -494,7 +494,9 @@ typedef struct } QCC_sref_t; typedef struct qcc_statement_s { - unsigned int op; + unsigned short op; + #define STF_LOGICOP (1u<<0) //do not bother following when looking for uninitialised variables. + unsigned short flags; QCC_sref_t a, b, c; unsigned int linenum; } QCC_statement_t; diff --git a/engine/qclib/qcc.h b/engine/qclib/qcc.h index c7305bc2..bef1d002 100644 --- a/engine/qclib/qcc.h +++ b/engine/qclib/qcc.h @@ -636,6 +636,7 @@ extern pbool flag_allowuninit; extern pbool flag_cpriority; extern pbool flag_embedsrc; extern pbool flag_nopragmafileline; +extern pbool flag_utf8strings; extern pbool opt_overlaptemps; extern pbool opt_shortenifnots; @@ -912,6 +913,25 @@ enum { WARN_MAX }; +//ansi colour codes, for debugging stuff. +enum +{ + COL_NONE, //white/regular text. + COL_ERROR, //to highlight errors + COL_WARNING, //to highlight warnings + COL_LOCATION, //to highlight file:line locations + COL_NAME, //unknown symbols. + COL_SYMBOL, //known symbols + COL_MAX +}; +extern const char *qcccol[COL_MAX]; +#define col_none qcccol[COL_NONE] +#define col_location qcccol[COL_LOCATION] +#define col_error qcccol[COL_ERROR] +#define col_name qcccol[COL_NAME] +#define col_warning qcccol[COL_WARNING] +#define col_symbol qcccol[COL_SYMBOL] + #define FLAG_KILLSDEBUGGERS 1 #define FLAG_ASDEFAULT 2 #define FLAG_SETINGUI 4 diff --git a/engine/qclib/qcc_pr_comp.c b/engine/qclib/qcc_pr_comp.c index 5fd9b9e6..3b92701f 100644 --- a/engine/qclib/qcc_pr_comp.c +++ b/engine/qclib/qcc_pr_comp.c @@ -128,6 +128,7 @@ pbool flag_cpriority; //operator precidence should adhere to C standards, inste pbool flag_allowuninit; //ignore uninitialised locals, avoiding all private locals. pbool flag_embedsrc; //embed all source files inside the .dat (can be opened with any zip program) pbool flag_nopragmafileline;//ignore #pragma file and #pragma line, so that I can actually read+debug xonotic's code. +pbool flag_utf8strings; //strings default to u8"" string rules. pbool opt_overlaptemps; //reduce numpr_globals by reuse of temps. When they are not needed they are freed for reuse. The way this is implemented is better than frikqcc's. (This is the single most important optimisation) pbool opt_assignments; //STORE_F isn't used if an operation wrote to a temp. @@ -3722,6 +3723,7 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ /*generate new OP_ADDRESS instruction - FIXME: the arguments may have changed since the original instruction*/ statement->op = OP_ADDRESS; + statement->flags = 0; statement->a = statements[st].a; statement->b = statements[st].b; statement->c = var_c->ofs; @@ -3729,6 +3731,7 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ /*convert old one to an OP_LOAD*/ statements[st].op = ((*op->type_c)->type==ev_vector)?OP_LOAD_V:OP_LOAD_F; + statement->flags = 0; // statements[st].a = statements[st].a; // statements[st].b = statements[st].b; // statements[st].c = statements[st].c; @@ -3805,6 +3808,7 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ //float pointer float temp = QCC_GetTemp(type_float); statement->op = OP_BITAND_F; + statement->flags = 0; statement->a = var_c ? var_c->ofs : 0; statement->b = var_a ? var_a->ofs : 0; statement->c = temp->ofs; @@ -3814,6 +3818,7 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ statement->linenum = pr_token_line_last; statement->op = OP_SUB_F; + statement->flags = 0; //t = c & i //c = c - t @@ -4155,6 +4160,7 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ statement->linenum = pr_token_line_last; statement->op = op - pr_opcodes; + statement->flags = 0; statement->a = var_a; statement->b = var_b; @@ -4238,6 +4244,7 @@ QCC_statement_t *QCC_PR_SimpleStatement ( QCC_opcode_t *op, QCC_sref_t var_a, QC numstatements++; statement->op = op - pr_opcodes; + statement->flags = 0; statement->a = var_a; statement->b = var_b; statement->c = var_c; @@ -4538,7 +4545,7 @@ void QCC_VerifyFormatString (const char *funcname, QCC_ref_t **arglist, unsigned { case 0: if (argpos < argcount && argn_last < argcount) - QCC_PR_ParseWarning(WARN_FORMATSTRING, "%s: surplus trailing arguments", funcname); + QCC_PR_ParseWarning(WARN_FORMATSTRING, "%s: surplus trailing arguments for format", funcname); return; case '%': if(*++s == '%') @@ -9863,7 +9870,7 @@ QCC_ref_t *QCC_PR_RefExpression (QCC_ref_t *retbuf, int priority, int exprflags) rhsr = QCC_PR_RefExpression (&rhsbuf, priority, exprflags | EXPR_DISALLOW_ARRAYASSIGN|EXPR_DISALLOW_COMMA); if (conditional&1) - QCC_PR_ParseWarning(WARN_ASSIGNMENTINCONDITIONAL, "suggest parenthesis for assignment used as truth value ."); + QCC_PR_ParseWarning(WARN_ASSIGNMENTINCONDITIONAL, "suggest parenthesis for assignment used as truth value"); rhsd = QCC_RefToDef(rhsr, true); @@ -10010,6 +10017,7 @@ QCC_ref_t *QCC_PR_RefExpression (QCC_ref_t *retbuf, int priority, int exprflags) if (logicjump) //logic shortcut jumps to just before the if. the rhs is uninitialised if the jump was taken, but the lhs makes it deterministic. { + logicjump->flags |= STF_LOGICOP; logicjump->b.ofs = &statements[numstatements] - logicjump; if (logicjump->b.ofs == 1) numstatements--; //err, that was pointless. @@ -12163,7 +12171,7 @@ int QCC_CheckOneUninitialised(int firststatement, int laststatement, QCC_def_t * return i; } } - else if (pr_opcodes[st->op].associative == ASSOC_RIGHT && (int)st->b.ofs > 0) + else if (pr_opcodes[st->op].associative == ASSOC_RIGHT && (int)st->b.ofs > 0 && !(st->flags & STF_LOGICOP)) { int jump = i + (int)st->b.ofs; //check if there's an else. @@ -13027,21 +13035,20 @@ QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_def_t *def, QCC_type_t *typ if (num_labels) num_labels = 0; - + if (num_cases) + { + num_cases = 0; + QCC_PR_ParseError(ERR_ILLEGALCASES, "%s: function contains illegal cases", f->name); + } if (num_continues) { num_continues=0; - QCC_PR_ParseError(ERR_ILLEGALCONTINUES, "%s: function contains illegal continues", pr_scope->name); + QCC_PR_ParseError(ERR_ILLEGALCONTINUES, "%s: function contains illegal continues", f->name); } if (num_breaks) { num_breaks=0; - QCC_PR_ParseError(ERR_ILLEGALBREAKS, "%s: function contains illegal breaks", pr_scope->name); - } - if (num_cases) - { - num_cases = 0; - QCC_PR_ParseError(ERR_ILLEGALCASES, "%s: function contains illegal cases", pr_scope->name); + QCC_PR_ParseError(ERR_ILLEGALBREAKS, "%s: function contains illegal breaks", f->name); } //clean up the locals. remove parms from the hashtable but don't clean subscoped_away so that we can repopulate on the next accumulation diff --git a/engine/qclib/qcc_pr_lex.c b/engine/qclib/qcc_pr_lex.c index a2c934f8..3a74074c 100644 --- a/engine/qclib/qcc_pr_lex.c +++ b/engine/qclib/qcc_pr_lex.c @@ -1577,8 +1577,7 @@ void QCC_PR_LexString (void) char *end, *cnst; int raw; char rawdelim[64]; - unsigned int code; - int stringtype = 0; //0 - quake output, input is 8bit. warnings when its not ascii. \u will still give utf-8 text, other chars as-is. Expect \s to screw everything up with utf-8 output. + int stringtype = flag_utf8strings?2:0; //0 - quake output, input is 8bit. warnings when its not ascii. \u will still give utf-8 text, other chars as-is. Expect \s to screw everything up with utf-8 output. //1 - quake output, input is utf-8. due to editors not supporting it, that generally means the input (ab)uses markup. //2 - utf-8 output, input is utf-8. welcome to the future! unfortunately not the present. @@ -1913,6 +1912,8 @@ void QCC_PR_LexString (void) unsigned int len = stringtype?utf8_check(pr_file_p-1, &cp):0; if (!len) { //invalid utf-8 encoding? don't treat it as utf-8! + if (stringtype) + QCC_PR_ParseWarning(ERR_BADCHARACTERCODE, "Input string is not valid utf-8"); c |= texttype; goto forcequake; } @@ -1996,8 +1997,9 @@ forcebyte: memcpy(pr_immediate_string, pr_token, len+1); pr_immediate_strlen = len; - if (qccwarningaction[WARN_NOTUTF8]) + /*if (qccwarningaction[WARN_NOTUTF8] && stringtype != 1) { + unsigned int code; size_t c; for (c = 0; c < pr_immediate_strlen; ) { @@ -2009,7 +2011,7 @@ forcebyte: } c += len; } - } + }*/ } #endif @@ -3866,9 +3868,9 @@ void QCC_PR_ParsePrintDef (int type, QCC_def_t *def) else if (def->isstatic) modifiers = "static "; if (flag_msvcstyle) - printf ("%s(%i) : %s%s %s is defined here\n", def->filen, def->s_line, modifiers, TypeName(def->type, tybuffer, sizeof(tybuffer)), def->name); + printf ("%s%s(%i) : %s%s%s %s%s%s is defined here\n", col_location, def->filen, def->s_line, col_none, modifiers, TypeName(def->type, tybuffer, sizeof(tybuffer)), col_symbol, def->name, col_none); else - printf ("%s:%i: %s%s %s is defined here\n", def->filen, def->s_line, modifiers, TypeName(def->type, tybuffer, sizeof(tybuffer)), def->name); + printf ("%s%s:%i: %s%s%s %s%s%s is defined here\n", col_location, def->filen, def->s_line, col_none, modifiers, TypeName(def->type, tybuffer, sizeof(tybuffer)), col_symbol, def->name, col_none); } } } @@ -3887,7 +3889,7 @@ static void QCC_PR_PrintMacro (qcc_includechunk_t *chunk) if (chunk->cnst) { #if 1 - printf ("%s:%i: %s is defined here\n", chunk->cnst->fromfile, chunk->cnst->fromline, chunk->cnst->name); + printf ("%s%s:%i: %s%s%s is defined here\n", col_location, chunk->cnst->fromfile, chunk->cnst->fromline, col_symbol, chunk->cnst->name, col_none); #else printf ("%s:%i: expanding %s\n", chunk->currentfilename, chunk->currentlinenumber, chunk->cnst->name); #endif @@ -3904,7 +3906,7 @@ void QCC_PR_PrintScope (void) if (pr_scope) { if (errorscope != pr_scope) - printf ("in function %s (line %i),\n", pr_scope->name, pr_scope->line); + printf ("in function %s%s%s (line %i),\n", col_symbol, pr_scope->name, col_none, pr_scope->line); errorscope = pr_scope; } else @@ -3944,9 +3946,9 @@ void VARGS QCC_PR_ParseError (int errortype, const char *error, ...) QCC_PR_PrintScope(); if (flag_msvcstyle) - printf ("%s(%i) : error: %s\n", s_filen, pr_source_line, string); + printf ("%s%s(%i) : %serror%s: %s\n", col_location, s_filen, pr_source_line, col_error, col_none, string); else - printf ("%s:%i: error: %s\n", s_filen, pr_source_line, string); + printf ("%s%s:%i: %serror%s: %s\n", col_location, s_filen, pr_source_line, col_error, col_none, string); longjmp (pr_parse_abort, 1); } @@ -3965,9 +3967,9 @@ void VARGS QCC_PR_ParseErrorPrintDef (int errortype, QCC_def_t *def, const char #endif QCC_PR_PrintScope(); if (flag_msvcstyle) - printf ("%s(%i) : error: %s\n", s_filen, pr_source_line, string); + printf ("%s%s(%i) : %serror%s: %s\n", col_location, s_filen, pr_source_line, col_error, col_none, string); else - printf ("%s:%i: error: %s\n", s_filen, pr_source_line, string); + printf ("%s%s:%i: %serror%s: %s\n", col_location, s_filen, pr_source_line, col_error, col_none, string); QCC_PR_ParsePrintDef(WARN_ERROR, def); @@ -3988,9 +3990,9 @@ void VARGS QCC_PR_ParseErrorPrintSRef (int errortype, QCC_sref_t def, const char #endif QCC_PR_PrintScope(); if (flag_msvcstyle) - printf ("%s(%i) : error: %s\n", s_filen, pr_source_line, string); + printf ("%s%s(%i) : %serror%s: %s\n", col_location, s_filen, pr_source_line, col_error, col_none, string); else - printf ("%s:%i: error: %s\n", s_filen, pr_source_line, string); + printf ("%s%s:%i: %serror%s: %s\n", col_location, s_filen, pr_source_line, col_error, col_none, string); QCC_PR_ParsePrintSRef(WARN_ERROR, def); @@ -4011,11 +4013,11 @@ pbool VARGS QCC_PR_PrintWarning (int type, const char *file, int line, const cha if (!string) ; else if (!file || !*file) - printf (":: error%s: %s\n", wnam, string); + printf (":: %serror%s%s: %s\n", col_error, wnam, col_none, string); else if (flag_msvcstyle) - printf ("%s(%i) : error%s: %s\n", file, line, wnam, string); + printf ("%s%s(%i) : %serror%s%s: %s\n", col_location, file, line, col_error, wnam, col_none, string); else - printf ("%s:%i: error%s: %s\n", file, line, wnam, string); + printf ("%s%s:%i: %serror%s%s: %s\n", col_location, file, line, col_error, wnam, col_none, string); pr_error_count++; } else if (qccwarningaction[type] == 2) @@ -4023,11 +4025,11 @@ pbool VARGS QCC_PR_PrintWarning (int type, const char *file, int line, const cha if (!string) ; else if (!file || !*file) - printf (":: werror%s: %s\n", wnam, string); + printf (":: %swerror%s%s: %s\n", col_error, wnam, col_none, string); else if (flag_msvcstyle) - printf ("%s(%i) : werror%s: %s\n", file, line, wnam, string); + printf ("%s%s(%i) : %swerror%s%s: %s\n", col_location, file, line, col_error, wnam, col_none, string); else - printf ("%s:%i: werror%s: %s\n", file, line, wnam, string); + printf ("%s%s:%i: %swerror%s%s: %s\n", col_location, file, line, col_error, wnam, col_none, string); pr_error_count++; } else @@ -4035,11 +4037,11 @@ pbool VARGS QCC_PR_PrintWarning (int type, const char *file, int line, const cha if (!string) ; else if (!file || !*file) - printf (":: warning%s: %s\n", wnam, string); + printf (":: %swarning%s%s: %s\n", col_warning, wnam, col_none, string); else if (flag_msvcstyle) - printf ("%s(%i) : warning%s: %s\n", file, line, wnam, string); + printf ("%s%s(%i) : %swarning%s%s: %s\n", col_location, file, line, col_warning, wnam, col_none, string); else - printf ("%s:%i: warning%s: %s\n", file, line, wnam, string); + printf ("%s%s:%i: %swarning%s%s: %s\n", col_location, file, line, col_warning, wnam, col_none, string); pr_warning_count++; } return true; @@ -4112,7 +4114,7 @@ Gets the next token void QCC_PR_Expect (const char *string) { if (STRCMP (string, pr_token)) - QCC_PR_ParseError (ERR_EXPECTED, "expected %s, found %s",string, pr_token); + QCC_PR_ParseError (ERR_EXPECTED, "expected %s%s%s, found %s%s%s", col_location, string, col_none, col_name, pr_token, col_none); QCC_PR_Lex (); } #endif @@ -4345,7 +4347,7 @@ char *QCC_PR_ParseName (void) if (pr_token_type == tt_eof) QCC_PR_ParseError (ERR_EOF, "unexpected EOF", pr_token); else if (strcmp(pr_token, "...")) //seriously? someone used '...' as an intrinsic NAME? - QCC_PR_ParseError (ERR_NOTANAME, "\"%s\" - not a name", pr_token); + QCC_PR_ParseError (ERR_NOTANAME, "\"%s%s%s\" - not a name", col_name, pr_token, col_none); } if (strlen(pr_token) >= MAX_NAME-1) QCC_PR_ParseError (ERR_NAMETOOLONG, "name too long"); diff --git a/engine/qclib/qccmain.c b/engine/qclib/qccmain.c index 7ab922fe..066839c7 100644 --- a/engine/qclib/qccmain.c +++ b/engine/qclib/qccmain.c @@ -387,7 +387,7 @@ compiler_flag_t compiler_flag[] = { {&flag_allowuninit, hideflag, "allowuninit", "Uninitialised Locals", "Permit optimisations that may result in locals being uninitialised. This may allow for greater reductions in temps."}, {&flag_nopragmafileline,FLAG_MIDCOMPILE,"nofileline", "Ignore #pragma file", "Ignores #pragma file(foo) and #pragma line(foo), so that errors and symbols reflect the actual lines, instead of the original source."}, // {&flag_lno, hidedefaultflag,"lno", "Gen Debugging Info", "Writes debugging info."}, -// {&flag_utf8, hidedefaultflag,"utf8", "Unicode", "Assume files to be UTF-8 encoded, instead of iso8859-1."}, + {&flag_utf8strings, FLAG_MIDCOMPILE,"utf8", "Unicode", "String immediates will use utf-8 encoding, instead of quake's encoding."}, {&flag_embedsrc, FLAG_MIDCOMPILE,"embedsrc", "Embed Sources", "Write the sourcecode into the output file. The resulting .dat can be opened as a standard zip archive (or by fteqccgui).\nGood for GPL compliance!"}, // {&flag_noreflection, FLAG_MIDCOMPILE,"omitinternals","Omit Reflection Info", "Keeps internal symbols private (equivelent to unix's hidden visibility). This has the effect of reducing filesize, thwarting debuggers, and breaking saved games. This allows you to use arrays without massively bloating the size of your progs.\nWARNING: The bit about breaking saved games was NOT a joke, but does not apply to menuqc or csqc. It also interferes with FTE_MULTIPROGS."}, @@ -1748,9 +1748,9 @@ pbool QCC_WriteData (int crc) QCC_PR_Warning(wt, def->filen, def->s_line, NULL); } else if (def->arraysize) - QCC_PR_Warning(wt, def->filen, def->s_line, (dupewarncount++ >= 10 && !verbose)?NULL:"%s %s[%i] no references.", TypeName(def->type, typestr, sizeof(typestr)), def->name, def->arraysize); + QCC_PR_Warning(wt, def->filen, def->s_line, (dupewarncount++ >= 10 && !verbose)?NULL:"%s %s%s%s[%i] no references.", TypeName(def->type, typestr, sizeof(typestr)), col_symbol, def->name, col_none, def->arraysize); else - QCC_PR_Warning(wt, def->filen, def->s_line, (dupewarncount++ >= 10 && !verbose)?NULL:"%s %s no references.", TypeName(def->type, typestr, sizeof(typestr)), def->name); + QCC_PR_Warning(wt, def->filen, def->s_line, (dupewarncount++ >= 10 && !verbose)?NULL:"%s %s%s%s no references.", TypeName(def->type, typestr, sizeof(typestr)), col_symbol, def->name, col_none); } pr_scope = NULL; @@ -1758,7 +1758,7 @@ pbool QCC_WriteData (int crc) { char typestr[256]; QCC_sref_t sr = {def, 0, def->type}; - QCC_PR_Warning(WARN_NOTREFERENCED, def->filen, def->s_line, "%s %s = %s used, but not referenced.", TypeName(def->type, typestr, sizeof(typestr)), def->name, QCC_VarAtOffset(sr)); + QCC_PR_Warning(WARN_NOTREFERENCED, def->filen, def->s_line, "%s %s%s%s = %s used, but not referenced.", TypeName(def->type, typestr, sizeof(typestr)), col_symbol, def->name, col_none, QCC_VarAtOffset(sr)); } /*if (opt_unreferenced && def->type->type != ev_field) { @@ -1959,7 +1959,7 @@ pbool QCC_WriteData (int crc) } if (dupewarncount > 10 && !verbose) - QCC_PR_Note(WARN_NOTREFERENCED, NULL, 0, "suppressed %i more warnings about precaches.", dupewarncount-10); + QCC_PR_Note(WARN_NOTREFERENCED, NULL, 0, "suppressed %i more %swarnings%s about precaches.", dupewarncount-10, col_warning, col_none); //PrintStrings (); //PrintFunctions (); @@ -2462,7 +2462,7 @@ strofs = (strofs+3)&~3; if (!SafeClose (h)) { - printf("Error while writing output %s\n", destfile); + printf("%sError%s while writing output %s\n", col_error, col_none, destfile); return false; } @@ -4268,8 +4268,6 @@ void QCC_PR_CommandLinePrecompilerOptions (void) opt_filenames = false; } } - else if (!stricmp(arg, "utf8")) - ; //we always interpret input as utf-8, and thus output strings are 'utf-8' too. -fno-utf8 might be useful to asciify inputs, but that'll just break quake-encoded text, so why bother else if (!stricmp(arg, "return-assignments")) ; //should really be a warning instead else if (!stricmp(arg, "relaxed-switch")) @@ -4650,6 +4648,8 @@ void QCC_GenerateRelativePath(char *dest, size_t destsize, char *base, char *rel } } +const char *qcccol[COL_MAX]; + int qcc_compileactive = false; extern int accglobalsblock; char *originalqccmsrc; //for autoprototype. @@ -4665,6 +4665,11 @@ pbool QCC_main (int argc, char **argv) //as part of the quake engine char *s; + //make sure any print colours are set up properly. + for (p = 0; p < COL_MAX; p++) + if (!qcccol[p]) + qcccol[p] = ""; + s_filen = "cmdline"; s_filed = 0; pr_source_line = 0; @@ -4706,7 +4711,7 @@ pbool QCC_main (int argc, char **argv) //as part of the quake engine MAX_STRINGS = 1<<21; MAX_GLOBALS = 1<<17; MAX_FIELDS = 1<<13; - MAX_STATEMENTS = 0x100000; + MAX_STATEMENTS = 1<<21; MAX_FUNCTIONS = 1<<15; maxtypeinfos = 1<<16; MAX_CONSTANTS = 1<<12; diff --git a/engine/qclib/qcctui.c b/engine/qclib/qcctui.c index 6b79f372..19cf6df4 100644 --- a/engine/qclib/qcctui.c +++ b/engine/qclib/qcctui.c @@ -3,6 +3,10 @@ #include #include +#if defined(__linux__) || defined(__unix__) +#include +#endif + /* ============== LoadFile @@ -126,6 +130,22 @@ int main (int argc, char **argv) funcs.funcs.parms->WriteFile = QCC_WriteFile; funcs.funcs.parms->Printf = logprintf; funcs.funcs.parms->Sys_Error = Sys_Error; + +#if defined(__linux__) || defined(__unix__) + if (isatty(STDOUT_FILENO)) + { //only use colours if its a tty, and not if we're redirected. + col_none = "\e[0;m"; //reset to white + col_error = "\e[0;31m"; //red + col_symbol = "\e[0;32m"; //green + col_warning = "\e[0;33m"; //yellow + //col_ = "\e[0;34m"; //blue + col_name = "\e[0;35m"; //magenta + //col_ = "\e[0;36m"; //cyan + col_location = "\e[0;1;37m"; //bright white + } +#endif + + #ifdef _WIN32 logfile = fopen("fteqcc.log", "wt"); #endif