$(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
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 */
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;
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 (!*out)
+ return false;
+
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;
+ /* 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;
}
- *out = ir_block_create_unary(func->curblock, ast_ctx(self),
- ast_function_label(func, "sce_final_not"),
- notop,
- *out);
}
- if (!*out)
- return false;
+
self->expression.outr = *out;
return true;
}
-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.
.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
.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
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) {
"typedef",
"goto",
- "noreturn",
-
"__builtin_debug_printtype"
};
static size_t num_keywords_fg = sizeof(keywords_fg) / sizeof(keywords_fg[0]);
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 '?':
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);
TOKEN_DOTS, /* 3 dots, ... */
+ TOKEN_ATTRIBUTE_OPEN, /* [[ */
+ TOKEN_ATTRIBUTE_CLOSE, /* ]] */
+
TOKEN_STRINGCONST, /* not the typename but an actual "string" */
TOKEN_CHARCONST,
TOKEN_VECTORCONST,
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;
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);
GMQCC_DEFINE_FLAG(UNKNOWN_PRAGMAS)
GMQCC_DEFINE_FLAG(UNREACHABLE_CODE)
GMQCC_DEFINE_FLAG(CPP)
+ GMQCC_DEFINE_FLAG(UNKNOWN_ATTRIBUTE)
#endif
#ifdef GMQCC_TYPE_OPTIMIZATIONS
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;
}
}
}
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) {
}
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");
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;
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;
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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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');
+}
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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');
+}
#ifndef NORETURN
-#define NORETURN noreturn
+#define NORETURN [[noreturn]]
#endif
void print(...) = #1;
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]]
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]]
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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 );
+}
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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