diff --git a/Makefile b/Makefile index 94b25e3..b6ea92b 100644 --- a/Makefile +++ b/Makefile @@ -89,6 +89,7 @@ clean: $(OBJ_D) $(OBJ_C) $(OBJ_X): gmqcc.h opts.def main.o: lexer.h parser.o: ast.h lexer.h +lexer.o: lexer.h ast.o: ast.h ir.h ir.o: ir.h diff --git a/ast.c b/ast.c index e544e48..5dc131e 100644 --- a/ast.c +++ b/ast.c @@ -1699,7 +1699,7 @@ bool ast_binary_codegen(ast_binary *self, ast_function *func, bool lvalue, ir_va return true; } - if (OPTS_FLAG(SHORT_LOGIC) && + if ((OPTS_FLAG(SHORT_LOGIC) || OPTS_FLAG(PERL_LOGIC)) && (self->op == INSTR_AND || self->op == INSTR_OR)) { /* short circuit evaluation */ @@ -1707,64 +1707,42 @@ bool ast_binary_codegen(ast_binary *self, ast_function *func, bool lvalue, ir_va ir_block *from_left, *from_right; ir_instr *phi; size_t merge_id; - uint16_t notop; - - /* Note about casting to true boolean values: - * We use a single NOT for sub expressions, and an - * overall NOT at the end, and for that purpose swap - * all the jump conditions in order for the NOT to get - * doubled. - * ie: (a && b) usually becomes (!!a ? !!b : !!a) - * but we translate this to (!(!a ? !a : !b)) - */ + /* prepare end-block */ merge_id = vec_size(func->ir_func->blocks); - merge = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "sce_merge")); + merge = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "sce_merge")); + /* generate the left expression */ cgen = self->left->expression.codegen; if (!(*cgen)((ast_expression*)(self->left), func, false, &left)) return false; - if (!OPTS_FLAG(PERL_LOGIC)) { - notop = type_not_instr[left->vtype]; - if (notop == AINSTR_END) { - compile_error(ast_ctx(self), "don't know how to cast to bool..."); - return false; - } - left = ir_block_create_unary(func->curblock, ast_ctx(self), - ast_function_label(func, "sce_not"), - notop, - left); - } + /* remember the block */ from_left = func->curblock; + /* create a new block for the right expression */ other = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "sce_other")); - if ( !(self->op == INSTR_OR) != !OPTS_FLAG(PERL_LOGIC) ) { + if (self->op == INSTR_AND) { + /* on AND: left==true -> other */ if (!ir_block_create_if(func->curblock, ast_ctx(self), left, other, merge)) return false; } else { + /* on OR: left==false -> other */ if (!ir_block_create_if(func->curblock, ast_ctx(self), left, merge, other)) return false; } /* use the likely flag */ vec_last(func->curblock->instr)->likely = true; + /* enter the right-expression's block */ func->curblock = other; + /* generate */ cgen = self->right->expression.codegen; if (!(*cgen)((ast_expression*)(self->right), func, false, &right)) return false; - if (!OPTS_FLAG(PERL_LOGIC)) { - notop = type_not_instr[right->vtype]; - if (notop == AINSTR_END) { - compile_error(ast_ctx(self), "don't know how to cast to bool..."); - return false; - } - right = ir_block_create_unary(func->curblock, ast_ctx(self), - ast_function_label(func, "sce_not"), - notop, - right); - } + /* remember block */ from_right = func->curblock; + /* jump to the merge block */ if (!ir_block_create_jump(func->curblock, ast_ctx(self), merge)) return false; @@ -1772,23 +1750,50 @@ bool ast_binary_codegen(ast_binary *self, ast_function *func, bool lvalue, ir_va vec_push(func->ir_func->blocks, merge); func->curblock = merge; - phi = ir_block_create_phi(func->curblock, ast_ctx(self), ast_function_label(func, "sce_value"), TYPE_FLOAT); + phi = ir_block_create_phi(func->curblock, ast_ctx(self), + ast_function_label(func, "sce_value"), + self->expression.vtype); ir_phi_add(phi, from_left, left); ir_phi_add(phi, from_right, right); *out = ir_phi_value(phi); - if (!OPTS_FLAG(PERL_LOGIC)) { - notop = type_not_instr[(*out)->vtype]; - if (notop == AINSTR_END) { - compile_error(ast_ctx(self), "don't know how to cast to bool..."); - return false; - } - *out = ir_block_create_unary(func->curblock, ast_ctx(self), - ast_function_label(func, "sce_final_not"), - notop, - *out); - } if (!*out) return false; + + if (!OPTS_FLAG(PERL_LOGIC)) { + /* cast-to-bool */ + if (OPTS_FLAG(CORRECT_LOGIC) && (*out)->vtype == TYPE_VECTOR) { + *out = ir_block_create_unary(func->curblock, ast_ctx(self), + ast_function_label(func, "sce_bool_v"), + INSTR_NOT_V, *out); + if (!*out) + return false; + *out = ir_block_create_unary(func->curblock, ast_ctx(self), + ast_function_label(func, "sce_bool"), + INSTR_NOT_F, *out); + if (!*out) + return false; + } + else if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && (*out)->vtype == TYPE_STRING) { + *out = ir_block_create_unary(func->curblock, ast_ctx(self), + ast_function_label(func, "sce_bool_s"), + INSTR_NOT_S, *out); + if (!*out) + return false; + *out = ir_block_create_unary(func->curblock, ast_ctx(self), + ast_function_label(func, "sce_bool"), + INSTR_NOT_F, *out); + if (!*out) + return false; + } + else { + *out = ir_block_create_binop(func->curblock, ast_ctx(self), + ast_function_label(func, "sce_bool"), + INSTR_AND, *out, *out); + if (!*out) + return false; + } + } + self->expression.outr = *out; return true; } diff --git a/doc/gmqcc.1 b/doc/gmqcc.1 index 89605fe..8ccc6e0 100644 --- a/doc/gmqcc.1 +++ b/doc/gmqcc.1 @@ -116,7 +116,7 @@ them. -f\fIno-\fRcorrect-ternary .fi .in -.SH Warnings +.SH COMPILE WARNINGS .TP .B -Wunused-variable Generate a warning about variables which are declared but never used. @@ -238,7 +238,11 @@ or code after a call to a function marked as 'noreturn'. .B -Wdebug Enable some warnings added in order to help debugging in the compiler. You won't need this. -.SH Compile Flags +.B -Wunknown-attribute +Warn on an unknown attribute. The warning will inlclude only the first +token inside the enclosing attribute-brackets. This may change when +the actual attribute syntax is better defined. +.SH COMPILE FLAGS .TP .B -foverlap-locals Allow local variables to overlap with each other if they don't @@ -305,9 +309,9 @@ prevents components from being listed. .TP .B -fcorrect-logic Most QC compilers translate if(a_vector) directly as an IF on the -vector, which means only the x-component is checked. This causes all -non-float types to use an appropriate NOT instruction on all logic -operations and invert their use. +vector, which means only the x-component is checked. This causes +vectors to be cast to actual booleans via a NOT_V and, if necessary, a +NOT_F chained to it. .in +4 .nf if (a_vector) // becomes diff --git a/exec.c b/exec.c index 0b9b362..c1aa695 100644 --- a/exec.c +++ b/exec.c @@ -977,6 +977,17 @@ int main(int argc, char **argv) printf("Program's system-checksum = 0x%04x\n", (unsigned int)prog->crc16); printf("Entity field space: %u\n", (unsigned int)prog->entityfields); printf("Globals: %u\n", (unsigned int)vec_size(prog->globals)); + printf("Counts:\n" + " code: %lu\n" + " defs: %lu\n" + " fields: %lu\n" + " functions: %lu\n" + " strings: %lu\n", + (unsigned long)vec_size(prog->code), + (unsigned long)vec_size(prog->defs), + (unsigned long)vec_size(prog->fields), + (unsigned long)vec_size(prog->functions), + (unsigned long)vec_size(prog->strings)); } if (opts_info) { diff --git a/lexer.c b/lexer.c index cf554fb..e506aec 100644 --- a/lexer.c +++ b/lexer.c @@ -50,8 +50,6 @@ static const char *keywords_fg[] = { "typedef", "goto", - "noreturn", - "__builtin_debug_printtype" }; static size_t num_keywords_fg = sizeof(keywords_fg) / sizeof(keywords_fg[0]); @@ -1117,6 +1115,15 @@ int lex_do(lex_file *lex) switch (ch) { case '[': + nextch = lex_getch(lex); + if (nextch == '[') { + lex_tokench(lex, ch); + lex_tokench(lex, nextch); + lex_endtoken(lex); + return (lex->tok.ttype = TOKEN_ATTRIBUTE_OPEN); + } + lex_ungetch(lex, nextch); + /* FALL THROUGH */ case '(': case ':': case '?': @@ -1126,11 +1133,23 @@ int lex_do(lex_file *lex) return (lex->tok.ttype = ch); else return (lex->tok.ttype = TOKEN_OPERATOR); + + case ']': + if (lex->flags.noops) { + nextch = lex_getch(lex); + if (nextch == ']') { + lex_tokench(lex, ch); + lex_tokench(lex, nextch); + lex_endtoken(lex); + return (lex->tok.ttype = TOKEN_ATTRIBUTE_CLOSE); + } + lex_ungetch(lex, nextch); + } + /* FALL THROUGH */ case ')': case ';': case '{': case '}': - case ']': case '#': lex_tokench(lex, ch); diff --git a/lexer.h b/lexer.h index e419794..e549373 100644 --- a/lexer.h +++ b/lexer.h @@ -73,6 +73,9 @@ enum { TOKEN_DOTS, /* 3 dots, ... */ + TOKEN_ATTRIBUTE_OPEN, /* [[ */ + TOKEN_ATTRIBUTE_CLOSE, /* ]] */ + TOKEN_STRINGCONST, /* not the typename but an actual "string" */ TOKEN_CHARCONST, TOKEN_VECTORCONST, diff --git a/main.c b/main.c index d732ef9..107098a 100644 --- a/main.c +++ b/main.c @@ -508,6 +508,11 @@ int main(int argc, char **argv) { return usage(); } + if (OPTS_FLAG(TRUE_EMPTY_STRINGS) && OPTS_FLAG(FALSE_EMPTY_STRINGS)) { + con_err("-ftrue-empty-strings and -ffalse-empty-strings are mutually exclusive"); + exit(1); + } + /* the standard decides which set of operators to use */ if (opts.standard == COMPILER_GMQCC) { operators = c_operators; diff --git a/opts.c b/opts.c index 69e93b4..5c48286 100644 --- a/opts.c +++ b/opts.c @@ -53,6 +53,7 @@ static void opts_setdefault() { opts_set(opts.warn, WARN_UNKNOWN_PRAGMAS, true); opts_set(opts.warn, WARN_UNREACHABLE_CODE, true); opts_set(opts.warn, WARN_CPP, true); + opts_set(opts.warn, WARN_UNKNOWN_ATTRIBUTE, true); /* flags */ opts_set(opts.flags, ADJUST_VECTOR_FIELDS, true); opts_set(opts.flags, FTEPP, false); diff --git a/opts.def b/opts.def index 9e53ff5..697afdc 100644 --- a/opts.def +++ b/opts.def @@ -73,6 +73,7 @@ GMQCC_DEFINE_FLAG(UNKNOWN_PRAGMAS) GMQCC_DEFINE_FLAG(UNREACHABLE_CODE) GMQCC_DEFINE_FLAG(CPP) + GMQCC_DEFINE_FLAG(UNKNOWN_ATTRIBUTE) #endif #ifdef GMQCC_TYPE_OPTIMIZATIONS diff --git a/parser.c b/parser.c index d647415..16d783a 100644 --- a/parser.c +++ b/parser.c @@ -890,23 +890,27 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) parseerror(parser, "invalid types for logical operation with -fperl-logic: %s and %s", ty1, ty2); return false; } - if (OPTS_FLAG(CORRECT_LOGIC)) { - /* non-floats need to be NOTed */ - for (i = 0; i < 2; ++i) { - if (exprs[i]->expression.vtype != TYPE_FLOAT) { - if (type_not_instr[exprs[i]->expression.vtype] == AINSTR_END) { - ast_type_to_string(exprs[0], ty1, sizeof(ty1)); - ast_type_to_string(exprs[1], ty2, sizeof(ty2)); - parseerror(parser, "invalid types for logical operation with -fcorrect-logic: %s and %s", ty1, ty2); - return false; - } - out = (ast_expression*)ast_unary_new(ctx, type_not_instr[exprs[i]->expression.vtype], exprs[i]); - if (!out) break; - out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, out); - if (!out) break; - exprs[i] = out; out = NULL; - if (OPTS_FLAG(PERL_LOGIC)) { - } + for (i = 0; i < 2; ++i) { + if (OPTS_FLAG(CORRECT_LOGIC) && exprs[i]->expression.vtype == TYPE_VECTOR) { + out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_V, exprs[i]); + if (!out) break; + out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, out); + if (!out) break; + exprs[i] = out; out = NULL; + if (OPTS_FLAG(PERL_LOGIC)) { + /* here we want to keep the right expressions' type */ + break; + } + } + else if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && exprs[i]->expression.vtype == TYPE_STRING) { + out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_S, exprs[i]); + if (!out) break; + out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, out); + if (!out) break; + exprs[i] = out; out = NULL; + if (OPTS_FLAG(PERL_LOGIC)) { + /* here we want to keep the right expressions' type */ + break; } } } @@ -1899,7 +1903,8 @@ static ast_expression* process_condition(parser_t *parser, ast_expression *cond, ast_unary *unary; ast_expression *prev; - if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && cond->expression.vtype == TYPE_STRING) { + if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && cond->expression.vtype == TYPE_STRING) + { prev = cond; cond = (ast_expression*)ast_unary_new(ast_ctx(cond), INSTR_NOT_S, cond); if (!cond) { @@ -1909,22 +1914,15 @@ static ast_expression* process_condition(parser_t *parser, ast_expression *cond, } ifnot = !ifnot; } - if (OPTS_FLAG(CORRECT_LOGIC) && - !(cond->expression.vtype == TYPE_STRING && OPTS_FLAG(TRUE_EMPTY_STRINGS))) + else if (OPTS_FLAG(CORRECT_LOGIC) && cond->expression.vtype == TYPE_VECTOR) { - /* non-floats need to use NOT; except for strings on -ftrue-empty-strings */ - unary = (ast_unary*)cond; - if (!ast_istype(cond, ast_unary) || unary->op < INSTR_NOT_F || unary->op > INSTR_NOT_FNC) + /* vector types need to be cast to true booleans */ + ast_binary *bin = (ast_binary*)cond; + if (!OPTS_FLAG(PERL_LOGIC) || !ast_istype(cond, ast_binary) || !(bin->op == INSTR_AND || bin->op == INSTR_OR)) { - /* use the right NOT_ */ + /* in perl-logic, AND and OR take care of the -fcorrect-logic */ prev = cond; - cond = (ast_expression*)ast_unary_new(ast_ctx(cond), type_not_instr[cond->expression.vtype], cond); - - /* - * cppcheck: it thinks there is a possible null pointer dereference - * otherwise it would be "redundant" to check it ast_unary_new returned - * null, it's wrong. - */ + cond = (ast_expression*)ast_unary_new(ast_ctx(cond), INSTR_NOT_V, cond); if (!cond) { ast_unref(prev); parseerror(parser, "internal error: failed to process condition"); @@ -1936,7 +1934,6 @@ static ast_expression* process_condition(parser_t *parser, ast_expression *cond, unary = (ast_unary*)cond; while (ast_istype(cond, ast_unary) && unary->op == INSTR_NOT_F) - /*&& unary->operand->expression.vtype != TYPE_STRING) */ { cond = unary->operand; unary->operand = NULL; @@ -2352,9 +2349,48 @@ static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bo bool had_var = false; bool had_noref = false; bool had_noreturn = false; + bool had_attrib = false; + *cvq = CV_NONE; for (;;) { - if (!strcmp(parser_tokval(parser), "const")) + if (parser->tok == TOKEN_ATTRIBUTE_OPEN) { + had_attrib = true; + /* parse an attribute */ + if (!parser_next(parser)) { + parseerror(parser, "expected attribute after `[[`"); + *cvq = CV_WRONG; + return false; + } + if (!strcmp(parser_tokval(parser), "noreturn")) { + had_noreturn = true; + if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) { + parseerror(parser, "`noreturn` attribute has no parameters, expected `]]`"); + *cvq = CV_WRONG; + return false; + } + } + else if (!strcmp(parser_tokval(parser), "noref")) { + had_noref = true; + if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) { + parseerror(parser, "`noref` attribute has no parameters, expected `]]`"); + *cvq = CV_WRONG; + return false; + } + } + else + { + /* Skip tokens until we hit a ]] */ + (void)!parsewarning(parser, WARN_UNKNOWN_ATTRIBUTE, "unknown attribute starting with `%s`", parser_tokval(parser)); + while (parser->tok != TOKEN_ATTRIBUTE_CLOSE) { + if (!parser_next(parser)) { + parseerror(parser, "error inside attribute"); + *cvq = CV_WRONG; + return false; + } + } + } + } + else if (!strcmp(parser_tokval(parser), "const")) had_const = true; else if (!strcmp(parser_tokval(parser), "var")) had_var = true; @@ -2362,9 +2398,7 @@ static bool parse_var_qualifiers(parser_t *parser, bool with_local, int *cvq, bo had_var = true; else if (!strcmp(parser_tokval(parser), "noref")) had_noref = true; - else if (!strcmp(parser_tokval(parser), "noreturn")) - had_noreturn = true; - else if (!had_const && !had_var && !had_noref && !had_noreturn) { + else if (!had_const && !had_var && !had_noref && !had_noreturn && !had_attrib) { return false; } else diff --git a/tests/correct-logic-1-s.tmpl b/tests/correct-logic-1-s.tmpl new file mode 100644 index 0000000..e3f3451 --- /dev/null +++ b/tests/correct-logic-1-s.tmpl @@ -0,0 +1,12 @@ +I: correct-logic.qc +D: vector logic flags +T: -execute +C: -std=fteqcc -fshort-logic +M: ! & | i N +M: 0, 0 -> 1 0 0 0 1 +M: 0, x -> 1 0 1 0 1 +M: x, 0 -> 0 0 1 1 0 +M: x, x -> 0 1 1 1 0 +M: 0, y -> 1 0 0 0 1 +M: y, 0 -> 0 0 0 0 1 +M: y, y -> 0 0 0 0 1 diff --git a/tests/correct-logic-1.tmpl b/tests/correct-logic-1.tmpl new file mode 100644 index 0000000..b151f46 --- /dev/null +++ b/tests/correct-logic-1.tmpl @@ -0,0 +1,12 @@ +I: correct-logic.qc +D: vector logic flags +T: -execute +C: -std=fteqcc +M: ! & | i N +M: 0, 0 -> 1 0 0 0 1 +M: 0, x -> 1 0 1 0 1 +M: x, 0 -> 0 0 1 1 0 +M: x, x -> 0 1 1 1 0 +M: 0, y -> 1 0 0 0 1 +M: y, 0 -> 0 0 0 0 1 +M: y, y -> 0 0 0 0 1 diff --git a/tests/correct-logic-2-s.tmpl b/tests/correct-logic-2-s.tmpl new file mode 100644 index 0000000..a7ada6e --- /dev/null +++ b/tests/correct-logic-2-s.tmpl @@ -0,0 +1,12 @@ +I: correct-logic.qc +D: vector logic flags +T: -execute +C: -std=fteqcc -fcorrect-logic -fshort-logic +M: ! & | i N +M: 0, 0 -> 1 0 0 0 1 +M: 0, x -> 1 0 1 0 1 +M: x, 0 -> 0 0 1 1 0 +M: x, x -> 0 1 1 1 0 +M: 0, y -> 1 0 1 0 1 +M: y, 0 -> 0 0 1 1 0 +M: y, y -> 0 1 1 1 0 diff --git a/tests/correct-logic-2.tmpl b/tests/correct-logic-2.tmpl new file mode 100644 index 0000000..7ecd3df --- /dev/null +++ b/tests/correct-logic-2.tmpl @@ -0,0 +1,12 @@ +I: correct-logic.qc +D: vector logic flags +T: -execute +C: -std=fteqcc -fcorrect-logic +M: ! & | i N +M: 0, 0 -> 1 0 0 0 1 +M: 0, x -> 1 0 1 0 1 +M: x, 0 -> 0 0 1 1 0 +M: x, x -> 0 1 1 1 0 +M: 0, y -> 1 0 1 0 1 +M: y, 0 -> 0 0 1 1 0 +M: y, y -> 0 1 1 1 0 diff --git a/tests/correct-logic.qc b/tests/correct-logic.qc new file mode 100644 index 0000000..cb9a38d --- /dev/null +++ b/tests/correct-logic.qc @@ -0,0 +1,27 @@ +void print(...) = #1; +string ftos (float) = #2; + +float test_s_not (vector s) { return !s; } +float test_s_and (vector s, vector t) { return s && t; } +float test_s_or (vector s, vector t) { return s || t; } +float test_s_if (vector s) { if (s) return 1; return 0; } +float test_s_ifnot(vector s) { if not (s) return 1; return 0; } + +void test(vector s, vector t) { + print(ftos(!!test_s_not (s)), " "); + print(ftos(!!test_s_and (s, t)), " "); + print(ftos(!!test_s_or (s, t)), " "); + print(ftos(!!test_s_if (s)), " "); + print(ftos(!!test_s_ifnot(s)), "\n"); +} + +void main() { + print(" ! & | i N\n"); + print("0, 0 -> "); test('0 0 0', '0 0 0'); + print("0, x -> "); test('0 0 0', '1 0 0'); + print("x, 0 -> "); test('1 0 0', '0 0 0'); + print("x, x -> "); test('1 0 0', '1 0 0'); + print("0, y -> "); test('0 0 0', '0 1 0'); + print("y, 0 -> "); test('0 1 0', '0 0 0'); + print("y, y -> "); test('0 1 0', '0 1 0'); +} diff --git a/tests/correct-vs-short-1.tmpl b/tests/correct-vs-short-1.tmpl new file mode 100644 index 0000000..9a2ac9b --- /dev/null +++ b/tests/correct-vs-short-1.tmpl @@ -0,0 +1,14 @@ +I: correct-vs-short.qc +D: correct-logic vs short-logic without perl-logic +T: -execute +C: -std=fteqcc +M: X & | B +M: 0 0 0, 0 0 0 :: 0 0 0 +M: 0 0 0, 5 0 0 :: 0 2 1 +M: 5 0 0, 0 0 0 :: 0 2 1 +M: 5 0 0, 5 0 0 :: 2 2 2 +M: Y & | B +M: 0 0 0, 0 0 0 :: 0 0 0 +M: 0 0 0, 0 5 0 :: 0 0 0 +M: 0 5 0, 0 0 0 :: 0 0 0 +M: 0 5 0, 0 5 0 :: 0 0 0 diff --git a/tests/correct-vs-short-2.tmpl b/tests/correct-vs-short-2.tmpl new file mode 100644 index 0000000..09897e5 --- /dev/null +++ b/tests/correct-vs-short-2.tmpl @@ -0,0 +1,14 @@ +I: correct-vs-short.qc +D: correct-logic vs short-logic without perl-logic +T: -execute +C: -std=fteqcc -fcorrect-logic +M: X & | B +M: 0 0 0, 0 0 0 :: 0 0 0 +M: 0 0 0, 5 0 0 :: 0 2 1 +M: 5 0 0, 0 0 0 :: 0 2 1 +M: 5 0 0, 5 0 0 :: 2 2 2 +M: Y & | B +M: 0 0 0, 0 0 0 :: 0 0 0 +M: 0 0 0, 0 5 0 :: 0 2 1 +M: 0 5 0, 0 0 0 :: 0 2 1 +M: 0 5 0, 0 5 0 :: 2 2 2 diff --git a/tests/correct-vs-short-3.tmpl b/tests/correct-vs-short-3.tmpl new file mode 100644 index 0000000..787ae22 --- /dev/null +++ b/tests/correct-vs-short-3.tmpl @@ -0,0 +1,14 @@ +I: correct-vs-short.qc +D: correct-logic vs short-logic without perl-logic +T: -execute +C: -std=fteqcc -fcorrect-logic -fshort-logic +M: X & | B +M: 0 0 0, 0 0 0 :: 0 0 0 +M: 0 0 0, 5 0 0 :: 0 2 1 +M: 5 0 0, 0 0 0 :: 0 2 1 +M: 5 0 0, 5 0 0 :: 2 2 2 +M: Y & | B +M: 0 0 0, 0 0 0 :: 0 0 0 +M: 0 0 0, 0 5 0 :: 0 2 1 +M: 0 5 0, 0 0 0 :: 0 2 1 +M: 0 5 0, 0 5 0 :: 2 2 2 diff --git a/tests/correct-vs-short.qc b/tests/correct-vs-short.qc new file mode 100644 index 0000000..81f9b7a --- /dev/null +++ b/tests/correct-vs-short.qc @@ -0,0 +1,21 @@ +void print(...) = #1; +string ftos (float) = #2; + +void test(vector a, vector b) { + print(ftos((a && b) + (a && b)), " "); + print(ftos((a || b) + (a || b)), " "); + print(ftos((a && b) + (a || b)), "\n"); +} + +void main() { + print("X & | B\n"); + print("0 0 0, 0 0 0 :: "); test('0 0 0', '0 0 0'); + print("0 0 0, 5 0 0 :: "); test('0 0 0', '5 0 0'); + print("5 0 0, 0 0 0 :: "); test('5 0 0', '0 0 0'); + print("5 0 0, 5 0 0 :: "); test('5 0 0', '5 0 0'); + print("Y & | B\n"); + print("0 0 0, 0 0 0 :: "); test('0 0 0', '0 0 0'); + print("0 0 0, 0 5 0 :: "); test('0 0 0', '0 5 0'); + print("0 5 0, 0 0 0 :: "); test('0 5 0', '0 0 0'); + print("0 5 0, 0 5 0 :: "); test('0 5 0', '0 5 0'); +} diff --git a/tests/noreturn.qc b/tests/noreturn.qc index 1437b06..e56cc88 100644 --- a/tests/noreturn.qc +++ b/tests/noreturn.qc @@ -1,5 +1,5 @@ #ifndef NORETURN -#define NORETURN noreturn +#define NORETURN [[noreturn]] #endif void print(...) = #1; diff --git a/tests/noreturn1.tmpl b/tests/noreturn1.tmpl index db507dd..0f6aad1 100644 --- a/tests/noreturn1.tmpl +++ b/tests/noreturn1.tmpl @@ -1,4 +1,4 @@ I: noreturn.qc D: noreturn keyword - should work T: -compile -C: -std=fteqcc -Wall -Werror -DTEST=1 -DNORETURN=noreturn +C: -std=fteqcc -Wall -Werror -DTEST=1 -DNORETURN=[[noreturn]] diff --git a/tests/noreturn2.tmpl b/tests/noreturn2.tmpl index d5d73c2..8d7ad09 100644 --- a/tests/noreturn2.tmpl +++ b/tests/noreturn2.tmpl @@ -1,4 +1,4 @@ I: noreturn.qc D: noreturn keyword - should fail T: -compile -C: -std=fteqcc -Wall -Werror -DTEST=2 -DNORETURN=noreturn +C: -std=fteqcc -Wall -Werror -DTEST=2 -DNORETURN=[[noreturn]] diff --git a/tests/truth-flags-1-s.tmpl b/tests/truth-flags-1-s.tmpl new file mode 100644 index 0000000..770f306 --- /dev/null +++ b/tests/truth-flags-1-s.tmpl @@ -0,0 +1,14 @@ +I: truth-flags-2.qc +D: logic flags +T: -execute +C: -std=fteqcc -fshort-logic +M: ! & | i N +M: 'str', 'str' -> 0 1 1 1 0 +M: 'str', '' -> 0 1 1 1 0 +M: 'str', 0 -> 0 0 1 1 0 +M: '', 'str' -> 1 1 1 1 0 +M: '', '' -> 1 1 1 1 0 +M: '', 0 -> 1 0 1 1 0 +M: 0, 'str' -> 1 0 1 0 1 +M: 0, '' -> 1 0 1 0 1 +M: 0, 0 -> 1 0 0 0 1 diff --git a/tests/truth-flags-1.tmpl b/tests/truth-flags-1.tmpl new file mode 100644 index 0000000..7562374 --- /dev/null +++ b/tests/truth-flags-1.tmpl @@ -0,0 +1,14 @@ +I: truth-flags-2.qc +D: logic flags +T: -execute +C: -std=fteqcc +M: ! & | i N +M: 'str', 'str' -> 0 1 1 1 0 +M: 'str', '' -> 0 1 1 1 0 +M: 'str', 0 -> 0 0 1 1 0 +M: '', 'str' -> 1 1 1 1 0 +M: '', '' -> 1 1 1 1 0 +M: '', 0 -> 1 0 1 1 0 +M: 0, 'str' -> 1 0 1 0 1 +M: 0, '' -> 1 0 1 0 1 +M: 0, 0 -> 1 0 0 0 1 diff --git a/tests/truth-flags-2-s.tmpl b/tests/truth-flags-2-s.tmpl new file mode 100644 index 0000000..3de75eb --- /dev/null +++ b/tests/truth-flags-2-s.tmpl @@ -0,0 +1,14 @@ +I: truth-flags-2.qc +D: logic flags +T: -execute +C: -std=fteqcc -fshort-logic -ftrue-empty-strings +M: ! & | i N +M: 'str', 'str' -> 0 1 1 1 0 +M: 'str', '' -> 0 1 1 1 0 +M: 'str', 0 -> 0 0 1 1 0 +M: '', 'str' -> 0 1 1 1 0 +M: '', '' -> 0 1 1 1 0 +M: '', 0 -> 0 0 1 1 0 +M: 0, 'str' -> 1 0 1 0 1 +M: 0, '' -> 1 0 1 0 1 +M: 0, 0 -> 1 0 0 0 1 diff --git a/tests/truth-flags-2.qc b/tests/truth-flags-2.qc new file mode 100644 index 0000000..0c10708 --- /dev/null +++ b/tests/truth-flags-2.qc @@ -0,0 +1,30 @@ +void print(...) = #1; +string ftos (float) = #2; + +float test_s_not (string s) { return !s; } +float test_s_and (string s, string t) { return s && t; } +float test_s_or (string s, string t) { return s || t; } +float test_s_if (string s) { if (s) return 1; return 0; } +float test_s_ifnot(string s) { if not (s) return 1; return 0; } + +void test(string s, string t) { + print(ftos(!!test_s_not (s)), " "); + print(ftos(!!test_s_and (s, t)), " "); + print(ftos(!!test_s_or (s, t)), " "); + print(ftos(!!test_s_if (s)), " "); + print(ftos(!!test_s_ifnot(s)), "\n"); +} + +string nuls; +void main() { + print(" ! & | i N\n"); + print("'str', 'str' -> "); test("FULL", "FULL"); + print("'str', '' -> "); test("FULL", "" ); + print("'str', 0 -> "); test("FULL", nuls ); + print("'', 'str' -> "); test("", "FULL"); + print("'', '' -> "); test("", "" ); + print("'', 0 -> "); test("", nuls ); + print("0, 'str' -> "); test(nuls, "FULL"); + print("0, '' -> "); test(nuls, "" ); + print("0, 0 -> "); test(nuls, nuls ); +} diff --git a/tests/truth-flags-2.tmpl b/tests/truth-flags-2.tmpl new file mode 100644 index 0000000..d9bb543 --- /dev/null +++ b/tests/truth-flags-2.tmpl @@ -0,0 +1,14 @@ +I: truth-flags-2.qc +D: logic flags +T: -execute +C: -std=fteqcc -ftrue-empty-strings +M: ! & | i N +M: 'str', 'str' -> 0 1 1 1 0 +M: 'str', '' -> 0 1 1 1 0 +M: 'str', 0 -> 0 0 1 1 0 +M: '', 'str' -> 0 1 1 1 0 +M: '', '' -> 0 1 1 1 0 +M: '', 0 -> 0 0 1 1 0 +M: 0, 'str' -> 1 0 1 0 1 +M: 0, '' -> 1 0 1 0 1 +M: 0, 0 -> 1 0 0 0 1 diff --git a/tests/truth-flags-3-s.tmpl b/tests/truth-flags-3-s.tmpl new file mode 100644 index 0000000..1ebd56b --- /dev/null +++ b/tests/truth-flags-3-s.tmpl @@ -0,0 +1,14 @@ +I: truth-flags-2.qc +D: logic flags +T: -execute +C: -std=fteqcc -fshort-logic -ffalse-empty-strings +M: ! & | i N +M: 'str', 'str' -> 0 1 1 1 0 +M: 'str', '' -> 0 0 1 1 0 +M: 'str', 0 -> 0 0 1 1 0 +M: '', 'str' -> 1 0 1 0 1 +M: '', '' -> 1 0 0 0 1 +M: '', 0 -> 1 0 0 0 1 +M: 0, 'str' -> 1 0 1 0 1 +M: 0, '' -> 1 0 0 0 1 +M: 0, 0 -> 1 0 0 0 1 diff --git a/tests/truth-flags-3.tmpl b/tests/truth-flags-3.tmpl new file mode 100644 index 0000000..f166dda --- /dev/null +++ b/tests/truth-flags-3.tmpl @@ -0,0 +1,14 @@ +I: truth-flags-2.qc +D: logic flags +T: -execute +C: -std=fteqcc -ffalse-empty-strings +M: ! & | i N +M: 'str', 'str' -> 0 1 1 1 0 +M: 'str', '' -> 0 0 1 1 0 +M: 'str', 0 -> 0 0 1 1 0 +M: '', 'str' -> 1 0 1 0 1 +M: '', '' -> 1 0 0 0 1 +M: '', 0 -> 1 0 0 0 1 +M: 0, 'str' -> 1 0 1 0 1 +M: 0, '' -> 1 0 0 0 1 +M: 0, 0 -> 1 0 0 0 1